Guest User

Vysor code

a guest
May 23rd, 2016
4,130
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. function nextTick(cb) {
  2.     setTimeout(cb, 0)
  3. }
  4.  
  5. function make4Len16(len) {
  6.     var len16 = len.toString(16);
  7.     while (len16.length < 4) {
  8.         len16 = "0" + len16
  9.     }
  10.     return len16
  11. }
  12. var pendingFuncs;
  13. window.addEventListener("message", function() {
  14.     if (pendingFuncs) {
  15.         $.each(pendingFuncs, function(i, func) {
  16.             func()
  17.         });
  18.         pendingFuncs = null
  19.     }
  20. }, false);
  21.  
  22. function unsafeCallback(cb) {
  23.     return cb
  24. }
  25.  
  26. function ab2str(buf) {
  27.     if (buf.constructor.name == "ArrayBuffer") {
  28.         buf = new Uint8Array(buf)
  29.     }
  30.     return String.fromCharCode.apply(null, buf)
  31. }
  32.  
  33. function str2ab(str, buf, terminate) {
  34.     var len = str.length;
  35.     if (terminate) len++;
  36.     if (!buf) {
  37.         buf = new ArrayBuffer(len)
  38.     }
  39.     var bufView = new Uint8Array(buf);
  40.     if (terminate) bufView[str.length] = 0;
  41.     for (var i = 0, strLen = str.length; i < strLen; i++) {
  42.         bufView[i] = str.charCodeAt(i)
  43.     }
  44.     return buf
  45. }
  46. var slashN = "\n".charCodeAt(0);
  47.  
  48. function writeLine(socket, str, cb) {
  49.     socket.write(str2ab(str + "\n"), cb)
  50. }
  51.  
  52. function readLine(socket, cb) {
  53.     var pending = [];
  54.  
  55.     function readMore() {
  56.         socket.read(function(buffer) {
  57.             for (var i = 0; i < buffer.byteLength; i++) {
  58.                 if (buffer[i] == slashN) {
  59.                     var keep = buffer.subarray(0, i);
  60.                     pending.push(keep);
  61.                     var data = "";
  62.                     for (var b in pending) {
  63.                         b = pending[b];
  64.                         data += ab2str(b)
  65.                     }
  66.                     var remaining = buffer.subarray(i + 1);
  67.                     socket.unshift(remaining);
  68.                     cb(data);
  69.                     return
  70.                 }
  71.             }
  72.             pending.push(buffer);
  73.             readMore()
  74.         })
  75.     }
  76.     readMore()
  77. }
  78.  
  79. function readString(socket, cb) {
  80.     var str = "";
  81.     socket.onClose = function() {
  82.         cb(str)
  83.     };
  84.  
  85.     function reader(data) {
  86.         str += ab2str(data);
  87.         socket.read(reader)
  88.     }
  89.     socket.read(reader)
  90. }
  91.  
  92. function appendBuffer(buffer1, buffer2) {
  93.     var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
  94.     tmp.set(buffer1, 0);
  95.     tmp.set(buffer2, buffer1.byteLength);
  96.     return tmp
  97. }
  98. var timeThing = (new Date).getTime();
  99.  
  100. function timeTrace(stamp) {
  101.     var now = (new Date).getTime();
  102.     console.log(stamp + ": " + (now - timeThing));
  103.     timeThing = now
  104. }
  105.  
  106. function bufferToHex(buffer) {
  107.     var view = new Uint8Array(buffer);
  108.     var ret = "";
  109.     for (var b in view) {
  110.         b = view[b];
  111.         if (b < 16) ret += "0" + b.toString(16);
  112.         else ret += b.toString(16)
  113.     }
  114.     return ret
  115. }
  116.  
  117. function hexToBuffer(str) {
  118.     var buf = new ArrayBuffer(str.length / 2);
  119.     var view = new Uint8Array(buf);
  120.     for (var i = 0; i < str.length / 2; i++) {
  121.         var c = str.substr(i * 2, 2);
  122.         view[i] = parseInt(c, 16)
  123.     }
  124.     return buf
  125. }
  126.  
  127. function base64ToArrayBuffer(base64) {
  128.     var binary_string = window.atob(base64);
  129.     var len = binary_string.length;
  130.     var bytes = new Uint8Array(len);
  131.     for (var i = 0; i < len; i++) {
  132.         var ascii = binary_string.charCodeAt(i);
  133.         bytes[i] = ascii
  134.     }
  135.     return bytes.buffer
  136. }
  137.  
  138. function arrayBufferToBase64(buffer) {
  139.     var binary = "";
  140.     var bytes = new Uint8Array(buffer);
  141.     var len = bytes.byteLength;
  142.     for (var i = 0; i < len; i++) {
  143.         binary += String.fromCharCode(bytes[i])
  144.     }
  145.     return window.btoa(binary)
  146. }
  147. var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  148. var b64pad = "=";
  149.  
  150. function hex2b64(h) {
  151.     var i;
  152.     var c;
  153.     var ret = "";
  154.     for (i = 0; i + 3 <= h.length; i += 3) {
  155.         c = parseInt(h.substring(i, i + 3), 16);
  156.         ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63)
  157.     }
  158.     if (i + 1 == h.length) {
  159.         c = parseInt(h.substring(i, i + 1), 16);
  160.         ret += b64map.charAt(c << 2)
  161.     } else if (i + 2 == h.length) {
  162.         c = parseInt(h.substring(i, i + 2), 16);
  163.         ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4)
  164.     }
  165.     while ((ret.length & 3) > 0) {
  166.         ret += b64pad
  167.     }
  168.     return ret
  169. }
  170. if (!String.prototype.startsWith) {
  171.     Object.defineProperty(String.prototype, "startsWith", {
  172.         enumerable: false,
  173.         configurable: false,
  174.         writable: false,
  175.         value: function(searchString, position) {
  176.             position = position || 0;
  177.             return this.lastIndexOf(searchString, position) === position
  178.         }
  179.     })
  180. }
  181.  
  182. function getQueryVariable(variable, url) {
  183.     if (!url) url = window.location;
  184.     var query = url.search.substring(1);
  185.     var vars = query.split("&");
  186.     for (var i = 0; i < vars.length; i++) {
  187.         var pair = vars[i].split("=");
  188.         if (decodeURIComponent(pair[0]) == variable) {
  189.             return decodeURIComponent(pair[1])
  190.         }
  191.     }
  192. }
  193. Object.fromArray = function(arr) {
  194.     var ret = {};
  195.     for (var i in arr) {
  196.         var val = arr[i];
  197.         ret[val] = val
  198.     }
  199.     return ret
  200. };
  201. $.ajaxTransport("+binary", function(options, originalOptions, jqXHR) {
  202.     if (window.FormData && (options.dataType && options.dataType == "binary" || options.data && (window.ArrayBuffer && options.data instanceof ArrayBuffer || window.Blob && options.data instanceof Blob))) {
  203.         return {
  204.             send: function(headers, callback) {
  205.                 var xhr = new XMLHttpRequest,
  206.                     url = options.url,
  207.                     type = options.type,
  208.                     async = options.async || true,
  209.                     dataType = options.responseType || "blob",
  210.                     data = options.data || null,
  211.                     username = options.username || null,
  212.                     password = options.password || null;
  213.                 xhr.addEventListener("load", function() {
  214.                     var data = {};
  215.                     data[options.dataType] = xhr.response;
  216.                     callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders())
  217.                 });
  218.                 xhr.open(type, url, async, username, password);
  219.                 for (var i in headers) {
  220.                     xhr.setRequestHeader(i, headers[i])
  221.                 }
  222.                 xhr.responseType = dataType;
  223.                 xhr.send(data)
  224.             },
  225.             abort: function() {
  226.                 jqXHR.abort()
  227.             }
  228.         }
  229.     }
  230. });
  231.  
  232. function throttleTimeout(token, item, throttle, cb) {
  233.     if (token) {
  234.         clearTimeout(token.timeout)
  235.     } else {
  236.         token = {
  237.             items: []
  238.         }
  239.     }
  240.     token.timeout = setTimeout(function() {
  241.         cb(token.items);
  242.         token.items = []
  243.     }, throttle);
  244.     token.items.push(item);
  245.     return token
  246. }
  247.  
  248. function copyTextToClipboard(text) {
  249.     var textArea = document.createElement("textarea");
  250.     textArea.style.position = "fixed";
  251.     textArea.style.top = 0;
  252.     textArea.style.left = 0;
  253.     textArea.style.width = "2em";
  254.     textArea.style.height = "2em";
  255.     textArea.style.padding = 0;
  256.     textArea.style.border = "none";
  257.     textArea.style.outline = "none";
  258.     textArea.style.boxShadow = "none";
  259.     textArea.style.background = "transparent";
  260.     textArea.value = text;
  261.     document.body.appendChild(textArea);
  262.     textArea.select();
  263.     try {
  264.         var successful = document.execCommand("copy")
  265.     } catch (err) {
  266.         console.log("Oops, unable to copy")
  267.     }
  268.     document.body.removeChild(textArea)
  269. }
  270.  
  271. function showNotification(text) {
  272.     console.log("notification:", text);
  273.     var appName = chrome.runtime.getManifest().name;
  274.     chrome.notifications.create({
  275.         type: "basic",
  276.         iconUrl: "/icon.png",
  277.         title: appName,
  278.         message: text
  279.     })
  280. }
  281. var readers = {};
  282. if (window.chrome && window.chrome.sockets) {
  283.     chrome.sockets.tcp.onReceive.addListener(function(resultData) {
  284.         var socket = readers[resultData.socketId];
  285.         if (socket == null) return;
  286.         socket.dataReceived(new Uint8Array(resultData.data))
  287.     });
  288.     chrome.sockets.tcp.onReceiveError.addListener(function(resultData) {
  289.         var socket = readers[resultData.socketId];
  290.         if (socket == null) return;
  291.         socket.destroy();
  292.         socket.dataReceived(null)
  293.     })
  294. }
  295.  
  296. function Socket(options, cb) {
  297.     if (options.socketId) {
  298.         this.socketId = options.socketId;
  299.         readers[this.socketId] = this
  300.     } else {
  301.         chrome.sockets.tcp.create(function(createInfo) {
  302.             this.socketId = createInfo.socketId;
  303.             chrome.sockets.tcp.connect(this.socketId, options.host, options.port, function(result) {
  304.                 chrome.runtime.lastError;
  305.                 if (!result) {
  306.                     readers[createInfo.socketId] = this;
  307.                     cb(this)
  308.                 } else {
  309.                     this.destroy();
  310.                     cb(null)
  311.                 }
  312.             }.bind(this))
  313.         }.bind(this))
  314.     }
  315. }
  316. Socket.connect = function(options, cb) {
  317.     return new Socket(options, cb)
  318. };
  319. Socket.pump = function(s1, s2, cb) {
  320.     var writeDone = function() {
  321.         s1.read(reader)
  322.     }.bind(s1);
  323.     var reader = function(data) {
  324.         var buffer = data.buffer;
  325.         if (data.byteOffset || data.length != buffer.byteLength) {
  326.             buffer = buffer.slice(data.byteOffset, data.byteOffset + data.length)
  327.         }
  328.         s2.write(buffer, writeDone)
  329.     }.bind(s2);
  330.     s1.read(reader);
  331.     s1.onClose = cb
  332. };
  333. Socket.stream = function(s1, s2, cb) {
  334.     Socket.pump(s1, s2, function() {
  335.         s2.destroy();
  336.         if (cb) {
  337.             var tmp = cb;
  338.             cb = null;
  339.             tmp()
  340.         }
  341.     });
  342.     Socket.pump(s2, s1, function() {
  343.         s1.destroy();
  344.         if (cb) {
  345.             var tmp = cb;
  346.             cb = null;
  347.             tmp()
  348.         }
  349.     })
  350. };
  351. Socket.eat = function(s) {
  352.     function reader() {
  353.         s.read(reader)
  354.     }
  355.     reader()
  356. };
  357. Socket.prototype.init = function() {
  358.     chrome.sockets.tcp.onReceive.addListener(function(receiveInfo) {
  359.         if (acceptInfo.socketId != createInfo.socketId) {
  360.             return
  361.         }
  362.     })
  363. };
  364. Socket.prototype.read = function() {
  365.     if (this.pendingCallback) {
  366.         throw new Error("double callback")
  367.     }
  368.     if (this.closed && !this.pending) {
  369.         var cb = this.onClose;
  370.         if (cb) {
  371.             delete this.onClose;
  372.             cb()
  373.         }
  374.         return
  375.     }
  376.     var argc = 0;
  377.     if (arguments[argc].constructor.name == "Number") {
  378.         this.pendingLength = arguments[argc++]
  379.     } else {
  380.         this.pendingLength = 0
  381.     }
  382.     var cb = arguments[argc];
  383.     if (!this.pending || this.paused) {
  384.         this.pendingCallback = cb;
  385.         return
  386.     }
  387.     if (!this.pendingLength) {
  388.         this.pendingLength = this.buffered()
  389.     } else if (this.pendingLength > this.buffered()) {
  390.         this.pendingCallback = cb;
  391.         return
  392.     }
  393.     var data;
  394.     var totalRead = 0;
  395.     while (totalRead < this.pendingLength) {
  396.         var buf = this.pending.shift();
  397.         this.bufferedLength -= buf.length;
  398.         if (!this.pending.length) delete this.pending;
  399.         var add = buf;
  400.         var need = Math.min(add.byteLength, this.pendingLength - totalRead);
  401.         if (need != add.byteLength) {
  402.             var part = add.subarray(0, need);
  403.             var leftover = add.subarray(need);
  404.             this.unshift(leftover);
  405.             add = part
  406.         }
  407.         if (!data && add.byteLength != this.pendingLength) data = new Uint8Array(this.pendingLength);
  408.         if (data) {
  409.             data.set(add, totalRead)
  410.         } else {
  411.             data = add
  412.         }
  413.         totalRead += add.byteLength
  414.     }
  415.     cb(data)
  416. };
  417. Socket.prototype.write = function(data, cb) {
  418.     chrome.sockets.tcp.send(this.socketId, data, function(writeInfo) {
  419.         chrome.runtime.lastError;
  420.         if (!writeInfo || writeInfo.resultCode) {
  421.             return
  422.         }
  423.         if (writeInfo.bytesSent < data.byteLength) {
  424.             this.write(data.slice(writeInfo.bytesSent), cb)
  425.         } else {
  426.             cb()
  427.         }
  428.     }.bind(this))
  429. };
  430. Socket.prototype.destroy = function(data, cb) {
  431.     chrome.sockets.tcp.close(this.socketId, function() {
  432.         chrome.runtime.lastError
  433.     })
  434. };
  435. Socket.prototype.unshift = function(buffer) {
  436.     if (buffer.byteLength == 0) return;
  437.     if (!this.pending) this.pending = [buffer];
  438.     else this.pending.unshift(buffer);
  439.     if (!this.bufferedLength) this.bufferedLength = 0;
  440.     this.bufferedLength += buffer.length
  441. };
  442. Socket.prototype.dataReceived = function(payload) {
  443.     if (payload && payload.length) {
  444.         var arr = new Uint8Array(payload);
  445.         if (!this.pending) this.pending = [arr];
  446.         else this.pending.push(arr)
  447.     }
  448.     if (payload == null) {
  449.         this.closed = true
  450.     } else {
  451.         if (!this.bufferedLength) this.bufferedLength = 0;
  452.         this.bufferedLength += payload.length
  453.     }
  454.     if (this.paused || !this.pending || !this.pending.length) {
  455.         var cb = this.onClose;
  456.         if (this.closed && cb) {
  457.             delete this.onClose;
  458.             cb()
  459.         }
  460.         return
  461.     }
  462.     var pl = this.pendingLength;
  463.     var cb = this.pendingCallback;
  464.     if (cb) {
  465.         delete this.pendingCallback;
  466.         this.read(pl, cb)
  467.     }
  468. };
  469. Socket.prototype.buffered = function() {
  470.     return this.bufferedLength
  471. };
  472. Socket.prototype.pause = function() {
  473.     if (this.paused) {
  474.         return
  475.     }
  476.     this.paused = true;
  477.     this.onPause()
  478. };
  479. Socket.prototype.resume = function() {
  480.     if (!this.paused) {
  481.         return
  482.     }
  483.     this.paused = false;
  484.     this.onResume()
  485. };
  486. Socket.prototype.onResume = function() {
  487.     chrome.sockets.tcp.setPaused(this.socketId, false, function() {})
  488. };
  489. Socket.prototype.onPause = function() {
  490.     chrome.sockets.tcp.setPaused(this.socketId, true, function() {})
  491. };
  492.  
  493. function Server() {}
  494. Server.prototype.__proto__ = Socket.prototype;
  495. Server.prototype.destroy = function() {
  496.     chrome.sockets.tcpServer.close(this.socketId)
  497. };
  498. var listeners = {};
  499. if (window.chrome && window.chrome.sockets) {
  500.     chrome.sockets.tcpServer.onAccept.addListener(function(acceptInfo) {
  501.         chrome.sockets.tcp.setPaused(acceptInfo.clientSocketId, false);
  502.         var listener = listeners[acceptInfo.socketId];
  503.         if (listener == null) return;
  504.         listener(new Socket({
  505.             socketId: acceptInfo.clientSocketId
  506.         }))
  507.     })
  508. }
  509. Server.prototype.listen = function(args, cb, listening) {
  510.     var port;
  511.     var address;
  512.     if (args.constructor.name == "Number") {
  513.         port = args;
  514.         address = "0.0.0.0"
  515.     } else {
  516.         address = args.address;
  517.         port = args.port
  518.     }
  519.     chrome.sockets.tcpServer.create(function(createInfo) {
  520.         this.socketId = createInfo.socketId;
  521.         listeners[this.socketId] = cb;
  522.         chrome.sockets.tcpServer.listen(createInfo.socketId, address, port, function(result) {
  523.             chrome.runtime.lastError;
  524.             if (result) {
  525.                 this.destroy();
  526.                 if (listening) {
  527.                     listening(result)
  528.                 }
  529.                 return
  530.             }
  531.             chrome.sockets.tcpServer.getInfo(this.socketId, function(info) {
  532.                 this.localAddress = info.localAddress;
  533.                 this.localPort = info.localPort;
  534.                 if (listening) {
  535.                     listening(result)
  536.                 }
  537.             }.bind(this))
  538.         }.bind(this))
  539.     }.bind(this))
  540. };
  541.  
  542. function DummySocket(buffer) {
  543.     this.dataReceived(buffer);
  544.     this.dataReceived(null)
  545. }
  546. DummySocket.prototype.write = function(data, cb) {
  547.     throw new Error("write not supported on dummy socket")
  548. };
  549. DummySocket.prototype.destroy = function() {};
  550. DummySocket.prototype.buffered = Socket.prototype.buffered;
  551. DummySocket.prototype.unshift = Socket.prototype.unshift;
  552. DummySocket.prototype.dataReceived = Socket.prototype.dataReceived;
  553. DummySocket.prototype.read = Socket.prototype.read;
  554. DummySocket.prototype.pause = Socket.prototype.pause;
  555. DummySocket.prototype.resume = Socket.prototype.resume;
  556. DummySocket.prototype.buffered = Socket.prototype.buffered;
  557. DummySocket.prototype.onPause = function() {};
  558. DummySocket.prototype.onResume = function() {};
  559.  
  560. function FetchSocket(url, cb) {
  561.     this.promise = fetch(url).then(function(response) {
  562.         this.connected = true;
  563.         this.response = response;
  564.         this.reader = this.response.body.getReader();
  565.         this.reader.closed.then(function() {
  566.             if (this.onClose) this.dataReceived(null)
  567.         }.bind(this));
  568.         this.onResume();
  569.         cb(this)
  570.     }.bind(this), function(error) {
  571.         cb(null, error)
  572.     })
  573. }
  574. FetchSocket.connect = function(url, cb) {
  575.     new FetchSocket(url, cb)
  576. };
  577. FetchSocket.prototype.write = function(data, cb) {
  578.     throw new Error("write not supported on fetch socket")
  579. };
  580. FetchSocket.prototype.destroy = function() {
  581.     if (this.promise && this.promise.cancel) this.promise.cancel()
  582. };
  583. FetchSocket.prototype.unshift = Socket.prototype.unshift;
  584. FetchSocket.prototype.dataReceived = Socket.prototype.dataReceived;
  585. FetchSocket.prototype.read = Socket.prototype.read;
  586. FetchSocket.prototype.pause = Socket.prototype.pause;
  587. FetchSocket.prototype.resume = Socket.prototype.resume;
  588. FetchSocket.prototype.buffered = Socket.prototype.buffered;
  589. FetchSocket.prototype.onPause = function() {};
  590. FetchSocket.prototype.onResume = function() {
  591.     this.reader.read().then(function(chunk) {
  592.         if (!chunk.value) return;
  593.         this.dataReceived(chunk.value);
  594.         if (this.paused) {
  595.             return
  596.         }
  597.         this.onResume()
  598.     }.bind(this))
  599. };
  600.  
  601. function GcmRtcSocket(conn, dc) {
  602.     this.conn = conn;
  603.     this.dc = dc;
  604.     this.gotEof = false;
  605.     dc.onmessage = function(message) {
  606.         var ui = new Uint8Array(message.data);
  607.         var eof = ui[ui.byteLength - 1] == 1;
  608.         this.dataReceived(ui.subarray(0, ui.byteLength - 1));
  609.         if (eof) {
  610.             this.gotEof = true;
  611.             this.destroy()
  612.         }
  613.     }.bind(this);
  614.     dc.onclose = dc.onerror = this.destroy.bind(this);
  615.     this.needsBufferShim = true || parseInt(/Chrome\/(\d\d)/.exec(navigator.userAgent)[1]) < 46
  616. }
  617. GcmRtcSocket.prototype.buffered = Socket.prototype.buffered;
  618. GcmRtcSocket.prototype.unshift = Socket.prototype.unshift;
  619. GcmRtcSocket.prototype.dataReceived = Socket.prototype.dataReceived;
  620. GcmRtcSocket.prototype.read = Socket.prototype.read;
  621. GcmRtcSocket.prototype.pause = Socket.prototype.pause;
  622. GcmRtcSocket.prototype.resume = Socket.prototype.resume;
  623. GcmRtcSocket.prototype.buffered = Socket.prototype.buffered;
  624. GcmRtcSocket.prototype.writeable = function() {
  625.     var cb = this.writeCallback;
  626.     if (cb) {
  627.         delete this.writeCallback;
  628.         cb()
  629.     }
  630. };
  631. GcmRtcSocket.prototype.write = function(data, cb) {
  632.     if (!this.dc || this.dc.readyState != "open") {
  633.         this.destroy();
  634.         return
  635.     }
  636.     this.writeCallback = cb;
  637.     var packet = new Uint8Array(data.byteLength + 1);
  638.     packet.set(new Uint8Array(data));
  639.     this.dc.send(packet.buffer);
  640.     if (this.reentrantWrite) return;
  641.     try {
  642.         this.reentrantWrite = true;
  643.         while (this.writeCallback && (this.dc.bufferedAmount == 0 || this.needsBufferShim)) {
  644.             this.writeable()
  645.         }
  646.     } finally {
  647.         this.reentrantWrite = false
  648.     }
  649. };
  650. GcmRtcSocket.prototype.destroy = function() {
  651.     this.dataReceived(null);
  652.     if (this.dc != null) {
  653.         if (this.dc.readyState == "open") {
  654.             this.dc.send(new Uint8Array([1]));
  655.             if (this.gotEof) this.conn.recycleChannel(this.dc);
  656.             else this.conn.waitForEof(this.dc)
  657.         } else [];
  658.         this.dc = null
  659.     }
  660. };
  661.  
  662. function GcmRtcConnection(pc) {
  663.     this.pc = pc;
  664.     this.pc.oniceconnectionstatechange = function() {
  665.         if (this.pc.iceConnectionState == "disconnected" || this.pc.iceConnectionState == "closed") {
  666.             this.destroy()
  667.         }
  668.     }.bind(this)
  669. }
  670. GcmRtcConnection.prototype.waitForCommand = function(dc) {
  671.     dc.onmessage = function(message) {
  672.         if (message.data.byteLength == 1) return;
  673.         this.removeChannel(dc);
  674.         var command = ab2str(message.data);
  675.         var socket = new GcmRtcSocket(this, dc);
  676.         this.openSocket(command, socket)
  677.     }.bind(this)
  678. };
  679. GcmRtcConnection.prototype.compactChannels = function() {
  680.     if (this.channels && !this.channels.length) this.channels = null
  681. };
  682. GcmRtcConnection.prototype.removeChannel = function(dc) {
  683.     i;
  684.     if (!this.channels) return;
  685.     var i = this.channels.indexOf(dc);
  686.     if (i == -1) return;
  687.     this.channels.splice(i, 1);
  688.     this.compactChannels()
  689. };
  690. GcmRtcConnection.prototype.waitForEof = function(dc) {
  691.     dc.onmessage = function(message) {
  692.         var ui = new Uint8Array(message.data);
  693.         var eof = ui[ui.byteLength - 1] == 1;
  694.         if (eof) this.recycleChannel(dc)
  695.     }.bind(this)
  696. };
  697. GcmRtcConnection.prototype.recycleChannel = function(dc) {
  698.     if (!this.channels) this.channels = [];
  699.     this.channels.push(dc);
  700.     dc.onclose = dc.onerror = function() {
  701.         this.removeChannel(dc)
  702.     }.bind(this);
  703.     this.waitForCommand(dc)
  704. };
  705. GcmRtcConnection.prototype.addCandidates = function(message) {
  706.     for (var candidate in message.candidates) {
  707.         this.pc.addIceCandidate(new RTCIceCandidate(message.candidates[candidate]))
  708.     }
  709. };
  710. GcmRtcConnection.prototype.setupPinger = function(pinger) {
  711.     var timeout;
  712.  
  713.     function ping() {
  714.         pinger.send(str2ab("ping"));
  715.         timeout = setTimeout(ping, 1e3)
  716.     }
  717.     pinger.onmessage = function(ignored) {};
  718.     pinger.onclose = pinger.onerror = function() {
  719.         clearTimeout(timeout);
  720.         this.destroy()
  721.     }.bind(this);
  722.     ping()
  723. };
  724. GcmRtcConnection.prototype.listenSockets = function() {
  725.     this.pc.ondatachannel = function(ev) {
  726.         this.waitForCommand(ev.channel)
  727.     }.bind(this)
  728. };
  729. GcmRtcConnection.prototype.prepareChannel = function(label) {
  730.     var dc = this.pc.createDataChannel(label || "gcm", {
  731.         reliable: true,
  732.         ordered: true
  733.     });
  734.     dc.binaryType = "arraybuffer";
  735.     return dc
  736. };
  737. GcmRtcConnection.prototype.newSocket = function(label, connectCallback) {
  738.     if (this.channels) {
  739.         var dc = this.channels.shift();
  740.         this.compactChannels();
  741.         dc.send(str2ab(label));
  742.         var socket = new GcmRtcSocket(this, dc);
  743.         connectCallback(socket, this);
  744.         return
  745.     }
  746.     var dc = this.prepareChannel("gcm");
  747.     dc.onopen = function() {
  748.         dc.send(str2ab(label));
  749.         var socket = new GcmRtcSocket(this, dc);
  750.         connectCallback(socket, this)
  751.     }.bind(this)
  752. };
  753. GcmRtcConnection.prototype.destroy = function() {
  754.     if (this.pc.signalingState != "closed") {
  755.         this.pc.close()
  756.     }
  757.     var cb = this.onClose;
  758.     if (cb) {
  759.         delete this.onClose;
  760.         cb()
  761.     }
  762. };
  763.  
  764. function GcmRtcManager(senderId, authorization, registrationId, rtcc) {
  765.     this.senderId = senderId;
  766.     this.registrationId = registrationId;
  767.     this.authorization = authorization;
  768.     this.rtcc = rtcc
  769. }
  770. GcmRtcManager.gcmRtcConnections = {};
  771. GcmRtcManager.onMessage = function(data) {
  772.     var message = JSON.parse(data.message);
  773.     var type = data.type;
  774.     var src = data.src;
  775.     var srcPort = data.srcPort;
  776.     var dstPort = data.dstPort;
  777.     if (type == "offer") {
  778.         var listener = GcmRtcManager.gcmRtcListeners[dstPort];
  779.         if (!listener) console.log("not listening on " + dstPort);
  780.         else listener.listener.incoming(src, srcPort, dstPort, message, listener.listenCallback);
  781.         return
  782.     } else if (type == "answer") {
  783.         var key = GcmRtcManager.getKey(src, srcPort, dstPort);
  784.         var conn = GcmRtcManager.gcmRtcConnections[key];
  785.         if (!conn) {
  786.             console.log("pending connection not found");
  787.             return
  788.         }
  789.         conn.manager.incoming(src, srcPort, dstPort, message);
  790.         return
  791.     }
  792.     console.log("unknown message " + type)
  793. };
  794. GcmRtcManager.hasLoadedChannels = false;
  795. GcmRtcManager.start = function(senderId, authorization, rtcc, cb) {
  796.     if (window.chrome && window.chrome.gcm) {
  797.         chrome.gcm.register([senderId], function(registrationId) {
  798.             console.log("gcm registration " + registrationId);
  799.             if (!registrationId) {
  800.                 cb();
  801.                 return
  802.             }
  803.             var s = new GcmRtcManager(senderId, authorization, registrationId, rtcc);
  804.             cb(s)
  805.         })
  806.     } else {
  807.         function listenChannel() {
  808.             $.ajax({
  809.                 type: "GET",
  810.                 url: "https://vysor-1026.appspot.com/listen",
  811.                 success: function(data) {
  812.                     console.log(data);
  813.                     var channel = new goog.appengine.Channel(data.token);
  814.                     var handler = {
  815.                         onopen: function() {
  816.                             var s = new GcmRtcManager(senderId, authorization, "web:" + data.channel, rtcc);
  817.                             cb(s)
  818.                         },
  819.                         onmessage: function(data) {
  820.                             GcmRtcManager.onMessage(JSON.parse(data.data))
  821.                         },
  822.                         onerror: function() {
  823.                             console.log("error", arguments)
  824.                         },
  825.                         onclose: function() {
  826.                             console.log("onclose", arguments)
  827.                         }
  828.                     };
  829.                     var socket = channel.open(handler)
  830.                 }
  831.             })
  832.         }
  833.         if (GcmRtcManager.hasLoadedChannels) {
  834.             listenChannel()
  835.         } else {
  836.             $.getScript("https://vysor-1026.appspot.com/_ah/channel/jsapi", listenChannel)
  837.         }
  838.     }
  839.     if (window.chrome && window.chrome.gcm) {
  840.         chrome.gcm.onMessage.addListener(function(data) {
  841.             GcmRtcManager.onMessage(data.data)
  842.         })
  843.     }
  844. };
  845. GcmRtcManager.prototype.sendGcm = function(registrationId, dstPort, srcPort, type, message) {
  846.     if (registrationId.startsWith("web:")) {
  847.         $.ajax({
  848.             type: "POST",
  849.             url: "https://vysor-1026.appspot.com/send",
  850.             data: JSON.stringify({
  851.                 channel: registrationId.substring(4),
  852.                 data: {
  853.                     src: this.registrationId,
  854.                     srcPort: srcPort,
  855.                     dstPort: dstPort,
  856.                     type: type,
  857.                     message: JSON.stringify(message)
  858.                 }
  859.             }),
  860.             contentType: "application/json",
  861.             dataType: "json",
  862.             success: function() {}
  863.         })
  864.     } else {
  865.         $.ajax({
  866.             type: "POST",
  867.             url: "https://gcm-http.googleapis.com/gcm/send",
  868.             headers: {
  869.                 Authorization: "key=" + this.authorization
  870.             },
  871.             data: JSON.stringify({
  872.                 to: registrationId,
  873.                 data: {
  874.                     src: this.registrationId,
  875.                     srcPort: srcPort,
  876.                     dstPort: dstPort,
  877.                     type: type,
  878.                     message: JSON.stringify(message)
  879.                 }
  880.             }),
  881.             contentType: "application/json",
  882.             dataType: "json",
  883.             success: function() {}
  884.         })
  885.     }
  886. };
  887. GcmRtcManager.getKey = function(registrationId, dstPort, srcPort) {
  888.     return srcPort + ":" + dstPort + ":" + registrationId
  889. };
  890. GcmRtcManager.prototype.setupPeerConnection = function(type, registrationId, dstPort, srcPort, getDesc) {
  891.     var RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection;
  892.     var pc = new RTCPeerConnection(this.rtcc);
  893.     var token;
  894.     var sendConnect = function(candidates) {
  895.         this.sendGcm(registrationId, dstPort, srcPort, type, {
  896.             desc: getDesc(),
  897.             candidates: candidates
  898.         })
  899.     }.bind(this);
  900.     pc.onicecandidate = function(evt) {
  901.         if (evt.candidate == null) return;
  902.         token = throttleTimeout(token, evt.candidate, 1e3, sendConnect)
  903.     }.bind(this);
  904.     var key = GcmRtcManager.getKey(registrationId, dstPort, srcPort);
  905.     var conn = new GcmRtcConnection(pc);
  906.     conn.manager = this;
  907.     var stableToken;
  908.     pc.onsignalingstatechange = function(ev) {
  909.         if (pc.signalingState == "stable") {
  910.             if (GcmRtcManager.gcmRtcConnections[key] == conn) {
  911.                 delete GcmRtcManager.gcmRtcConnections[key]
  912.             }
  913.         } else if (pc.signalingState == "closed") {
  914.             conn.destroy()
  915.         }
  916.     };
  917.     GcmRtcManager.gcmRtcConnections[key] = conn;
  918.     return conn
  919. };
  920. GcmRtcManager.gcmPortCount = 0;
  921. GcmRtcManager.prototype.connect = function(registrationId, port, connectCallback) {
  922.     var localPort = GcmRtcManager.gcmPortCount++;
  923.     var d;
  924.     var conn = this.setupPeerConnection("offer", registrationId, port, localPort, function() {
  925.         return d
  926.     }, connectCallback);
  927.     var pc = conn.pc;
  928.     var pinger = conn.prepareChannel("pinger");
  929.     pinger.onopen = function() {
  930.         conn.setupPinger(pinger);
  931.         connectCallback(this)
  932.     }.bind(conn);
  933.     conn.listenSockets();
  934.     pc.createOffer(function(desc) {
  935.         d = desc;
  936.         pc.setLocalDescription(desc)
  937.     }, function() {})
  938. };
  939. GcmRtcManager.gcmRtcListeners = {};
  940. GcmRtcManager.prototype.listen = function(port, cb) {
  941.     if (GcmRtcManager.gcmRtcListeners[port]) {
  942.         console.log("already listening on gcm port " + port);
  943.         return
  944.     }
  945.     GcmRtcManager.gcmRtcListeners[port] = {
  946.         listener: this,
  947.         listenCallback: cb
  948.     }
  949. };
  950. GcmRtcManager.prototype.incoming = function(src, srcPort, dstPort, message, listenCallback) {
  951.     var key = GcmRtcManager.getKey(src, srcPort, dstPort);
  952.     var conn = GcmRtcManager.gcmRtcConnections[key];
  953.     if (!conn) {
  954.         var d;
  955.         conn = this.setupPeerConnection("answer", src, srcPort, dstPort, function() {
  956.             return d
  957.         });
  958.         conn.remoteDesc = new RTCSessionDescription(message.desc);
  959.         var pc = conn.pc;
  960.         pc.setRemoteDescription(conn.remoteDesc, function() {
  961.             pc.createAnswer(function(answer) {
  962.                 d = answer;
  963.                 pc.setLocalDescription(answer)
  964.             }, function() {})
  965.         });
  966.         pc.ondatachannel = function(ev) {
  967.             this.setupPinger(ev.channel);
  968.             listenCallback(conn);
  969.             this.listenSockets()
  970.         }.bind(conn)
  971.     } else if (!conn.remoteDesc) {
  972.         conn.remoteDesc = new RTCSessionDescription(message.desc);
  973.         var pc = conn.pc;
  974.         pc.setRemoteDescription(conn.remoteDesc)
  975.     }
  976.     conn.addCandidates(message)
  977. };
  978.  
  979. function AdbUsbTransport(handle, iface) {
  980.     this.handle = handle;
  981.     this.iface = iface;
  982.     for (var endpoint in iface.endpoints) {
  983.         endpoint = iface.endpoints[endpoint];
  984.         if (endpoint.type == "bulk") {
  985.             this.zero_mask = endpoint.maximumPacketSize - 1;
  986.             if (endpoint.direction == "in") {
  987.                 this.in = endpoint
  988.             } else {
  989.                 this.out = endpoint
  990.             }
  991.         }
  992.     }
  993. }
  994. AdbUsbTransport.prototype.destroy = function() {
  995.     chrome.usb.releaseInterface(this.handle, this.iface.interfaceNumber, function() {
  996.         chrome.usb.closeDevice(this.handle, function() {})
  997.     }.bind(this))
  998. };
  999. AdbUsbTransport.prototype.write = function(data, callback) {
  1000.     if (this.writing) {
  1001.         if (!this.pendingWrites) this.pendingWrites = [];
  1002.         this.pendingWrites.push({
  1003.             data: data,
  1004.             callback: callback
  1005.         });
  1006.         return
  1007.     }
  1008.     var transfer = {
  1009.         direction: "out",
  1010.         endpoint: this.out.address,
  1011.         data: data
  1012.     };
  1013.     this.writing = true;
  1014.     chrome.usb.bulkTransfer(this.handle, transfer, function(result) {
  1015.         this.writing = false;
  1016.         callback(result);
  1017.         if (!this.pendingWrites) return;
  1018.         var next = this.pendingWrites.shift();
  1019.         if (!this.pendingWrites.length) this.pendingWrites = null;
  1020.         this.write(next.data, next.callback)
  1021.     }.bind(this))
  1022. };
  1023. AdbUsbTransport.prototype.read = function(len, cb) {
  1024.     var transfer = {
  1025.         direction: "in",
  1026.         endpoint: this.in.address,
  1027.         length: len
  1028.     };
  1029.     chrome.usb.bulkTransfer(this.handle, transfer, unsafeCallback(cb))
  1030. };
  1031.  
  1032. function AdbTcpTransport(socket) {
  1033.     this.socket = socket;
  1034.     this.zero_mask = (1 << 30) - 1
  1035. }
  1036. AdbTcpTransport.prototype.destroy = function() {
  1037.     this.socket.destroy()
  1038. };
  1039. AdbTcpTransport.prototype.write = function(data, callback) {
  1040.     if (this.writing) {
  1041.         if (!this.pendingWrites) this.pendingWrites = [];
  1042.         this.pendingWrites.push({
  1043.             data: data,
  1044.             callback: callback
  1045.         });
  1046.         return
  1047.     }
  1048.     this.writing = true;
  1049.     this.socket.write(data, function() {
  1050.         this.writing = false;
  1051.         callback({
  1052.             resultCode: 0
  1053.         });
  1054.         if (!this.pendingWrites) return;
  1055.         var next = this.pendingWrites.shift();
  1056.         if (!this.pendingWrites.length) this.pendingWrites = null;
  1057.         this.write(next.data, next.callback)
  1058.     }.bind(this))
  1059. };
  1060. AdbTcpTransport.prototype.read = function(len, cb) {
  1061.     this.socket.read(len, function(data) {
  1062.         cb({
  1063.             resultCode: 0,
  1064.             data: data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength)
  1065.         })
  1066.     })
  1067. };
  1068.  
  1069. function AdbDevice(transport, cb) {
  1070.     this.onConnected = cb;
  1071.     this.transport = transport;
  1072.     this.currentSocketId = 0;
  1073.     this.sockets = {};
  1074.     this.forwards = {};
  1075.     this.maxPayload = AdbDevice.MAX_PAYLOAD
  1076. }
  1077. AdbDevice.prototype.fatal = function(e) {
  1078.     console.log("fatal error", JSON.stringify(e));
  1079.     var cb = this.onConnected;
  1080.     if (cb) {
  1081.         delete this.onConnected;
  1082.         cb()
  1083.     } else if (this.onError) {
  1084.         this.onError();
  1085.         delete this.onError
  1086.     }
  1087.     this.destroy()
  1088. };
  1089. AdbDevice.prototype.destroy = function() {
  1090.     for (var socket in this.sockets) {
  1091.         socket = this.sockets[socket];
  1092.         socket.dataReceived(null)
  1093.     }
  1094.     if (this.forwards) {
  1095.         $.each(this.forwards, function(port, listener) {
  1096.             listener.destroy()
  1097.         })
  1098.     }
  1099.     this.transport.destroy()
  1100. };
  1101. AdbDevice.kCommandSYNC = 1129208147, AdbDevice.kCommandCNXN = 1314410051, AdbDevice.kCommandOPEN = 1313165391, AdbDevice.kCommandOKAY = 1497451343, AdbDevice.kCommandCLSE = 1163086915, AdbDevice.kCommandWRTE = 1163154007, AdbDevice.kCommandAUTH = 1213486401;
  1102. AdbDevice.kAuthToken = 1;
  1103. AdbDevice.kAuthSignature = 2;
  1104. AdbDevice.kAuthRSAPublicKey = 3;
  1105. AdbDevice.ADB_PROTOCOL_VERSION = 16777216;
  1106. AdbDevice.ADB_VERSION = 32;
  1107. AdbDevice.MAX_PAYLOAD = 4096;
  1108. AdbDevice.checksum = function(data) {
  1109.     data = new Uint8Array(data);
  1110.     var sum = 0;
  1111.     for (var i = 0; i < data.byteLength; i++) {
  1112.         sum += data[i]
  1113.     }
  1114.     return sum & 4294967295
  1115. };
  1116. AdbDevice.prototype.sendMessage = function(command, arg0, arg1, payload, cb) {
  1117.     if (!payload) {
  1118.         payload = ""
  1119.     }
  1120.     if (payload.constructor.name == "String") {
  1121.         payload = str2ab(payload)
  1122.     }
  1123.     var appendZero = true;
  1124.     if (!payload.byteLength) {
  1125.         appendZero = false
  1126.     }
  1127.     if (command == AdbDevice.kCommandAUTH && arg0 == AdbDevice.kAuthSignature) {
  1128.         appendZero = false
  1129.     }
  1130.     if (command == AdbDevice.kCommandWRTE) {
  1131.         appendZero = false
  1132.     }
  1133.     var bodyLength = payload.byteLength;
  1134.     if (appendZero) {
  1135.         bodyLength++
  1136.     }
  1137.     if (appendZero) {
  1138.         var copy = new ArrayBuffer(payload.byteLength + 1);
  1139.         var view = new Uint8Array(copy);
  1140.         view.set(new Uint8Array(payload));
  1141.         view[copy.byteLength - 1] = 0;
  1142.         payload = copy
  1143.     }
  1144.     var header = new ArrayBuffer(24);
  1145.     var view = new DataView(header);
  1146.     view.setUint32(0, command, true);
  1147.     view.setUint32(4, arg0, true);
  1148.     view.setUint32(8, arg1, true);
  1149.     view.setUint32(12, bodyLength, true);
  1150.     view.setUint32(16, AdbDevice.checksum(payload), true);
  1151.     view.setUint32(20, command ^ 4294967295, true);
  1152.     this.transport.write(header, function(result) {
  1153.         if (result.resultCode) {
  1154.             this.fatal(result)
  1155.         }
  1156.         if (!payload.byteLength && cb) {
  1157.             cb()
  1158.         }
  1159.     }.bind(this));
  1160.     if (payload.byteLength) {
  1161.         this.transport.write(payload, function(result) {
  1162.             if (result.resultCode) {
  1163.                 this.fatal(result)
  1164.             }
  1165.             if (cb) {
  1166.                 cb()
  1167.             }
  1168.         }.bind(this))
  1169.     }
  1170. };
  1171. AdbDevice.prototype.getKey = function(cb) {
  1172.     chrome.storage.local.get("adbkey", function(result) {
  1173.         var adbkey = result.adbkey;
  1174.         var key = new JSEncrypt({
  1175.             default_key_size: 2048
  1176.         });
  1177.         if (!adbkey) {
  1178.             adbkey = key.getPrivateKeyB64();
  1179.             key.setPrivateKey(adbkey);
  1180.             chrome.storage.local.set({
  1181.                 adbkey: adbkey
  1182.             })
  1183.         } else {
  1184.             key.setPrivateKey(adbkey)
  1185.         }
  1186.         cb(key)
  1187.     })
  1188. };
  1189. AdbDevice.prototype._convertToMinCrypt = function(rsaKey) {
  1190.     var bitLength = 2048;
  1191.     var numWords = bitLength / 8 / 4;
  1192.     var B32 = BigInteger.ONE.shiftLeft(32);
  1193.     var N = rsaKey.n.clone();
  1194.     var R = BigInteger.ONE.shiftLeft(1).pow(bitLength);
  1195.     var RR = R.multiply(R).mod(N);
  1196.     var pkey = new Uint32Array(3 + numWords * 2);
  1197.     pkey[0] = numWords;
  1198.     pkey[1] = B32.subtract(N.modInverse(B32)).intValue();
  1199.     var iEnd = numWords + 2;
  1200.     for (var i = 2, j = 2 + numWords; i < iEnd; ++i, ++j) {
  1201.         pkey[i] = N.mod(B32).intValue();
  1202.         N = N.divide(B32);
  1203.         pkey[j] = RR.mod(B32).intValue();
  1204.         RR = RR.divide(B32)
  1205.     }
  1206.     pkey[pkey.length - 1] = rsaKey.e;
  1207.     var hexStr = "";
  1208.     var u8view = new Uint8Array(pkey.buffer);
  1209.     for (var i = 0; i < u8view.length; ++i) {
  1210.         var digit = u8view[i].toString(16);
  1211.         if (digit.length == 1) {
  1212.             hexStr += "0"
  1213.         }
  1214.         hexStr += digit
  1215.     }
  1216.     return hex2b64(hexStr) + " adb@chrome"
  1217. };
  1218. AdbDevice.prototype.sign = function(rsakey, data) {
  1219.     if (rsakey == null) {
  1220.         throw "AuthManager is not initialized"
  1221.     }
  1222.     var totalLen = 2048 / 8;
  1223.     var array = new Uint8Array(totalLen);
  1224.     array[0] = 0;
  1225.     array[1] = 1;
  1226.     var ASN1_PREAMBLE = [0, 48, 33, 48, 9, 6, 5, 43, 14, 3, 2, 26, 5, 0, 4, 20];
  1227.     var padEnd = totalLen - ASN1_PREAMBLE.length - data.byteLength;
  1228.     for (var i = 2; i < padEnd; i++) {
  1229.         array[i] = 255
  1230.     }
  1231.     array.set(new Uint8Array(ASN1_PREAMBLE), padEnd);
  1232.     padEnd += ASN1_PREAMBLE.length;
  1233.     array.set(new Uint8Array(data), padEnd);
  1234.     var msg = new BigInteger(Array.apply([], array));
  1235.     return new Uint8Array(rsakey.doPrivate(msg).toByteArray()).buffer
  1236. };
  1237.  
  1238. function parseConnectionPayload(payload) {
  1239.     var ret = {};
  1240.     payload = ab2str(payload);
  1241.     var splits = payload.replace("device::", "").split(";");
  1242.     for (var split in splits) {
  1243.         split = splits[split];
  1244.         var parts = split.split("=");
  1245.         if (parts.length == 2) {
  1246.             ret[parts[0]] = parts[1]
  1247.         }
  1248.     }
  1249.     return ret
  1250. }
  1251. AdbDevice.prototype.handleUnknown = function(localId, remoteId) {
  1252.     console.log("no idea what this socket is.");
  1253.     this.sendMessage(AdbDevice.kCommandCLSE, localId, remoteId)
  1254. };
  1255. AdbDevice.prototype.receiveMessages = function() {
  1256.     this.transport.read(24, function(result) {
  1257.         if (result.resultCode) {
  1258.             this.fatal(result);
  1259.             return
  1260.         }
  1261.         var header = new DataView(result.data);
  1262.         var command = header.getUint32(0, true);
  1263.         var arg0 = header.getUint32(4, true);
  1264.         var arg1 = header.getUint32(8, true);
  1265.         var bodyLength = header.getUint32(12, true);
  1266.         var sum = header.getUint32(16, true);
  1267.         var transferLen = header.getUint32(12, true);
  1268.         var handleMessage = function(payload) {
  1269.             switch (command) {
  1270.                 case AdbDevice.kCommandOPEN:
  1271.                     if (this.onOpenSocket) this.onOpenSocket(payload, arg0);
  1272.                     break;
  1273.                 case AdbDevice.kCommandAUTH:
  1274.                     console.log("auth:", this);
  1275.                     this.getKey(function(rsakey) {
  1276.                         if (this.sentSignature) {
  1277.                             var publicKey = this._convertToMinCrypt(rsakey.getKey());
  1278.                             this.sendMessage(AdbDevice.kCommandAUTH, AdbDevice.kAuthRSAPublicKey, 0, publicKey);
  1279.                             showNotification('Check your Android device and click "Allow USB Debugging".')
  1280.                         } else {
  1281.                             this.sentSignature = true;
  1282.                             var signed = this.sign(rsakey.getKey(), payload);
  1283.                             this.sendMessage(AdbDevice.kCommandAUTH, AdbDevice.kAuthSignature, 0, signed, function() {})
  1284.                         }
  1285.                     }.bind(this));
  1286.                     break;
  1287.                 case AdbDevice.kCommandOKAY:
  1288.                     var remoteId = arg0;
  1289.                     var localId = arg1;
  1290.                     var socket = this.sockets[localId];
  1291.                     if (!socket) {
  1292.                         this.handleUnknown(localId, remoteId);
  1293.                         return
  1294.                     }
  1295.                     var cb = socket.onConnected;
  1296.                     if (cb) {
  1297.                         delete socket.onConnected;
  1298.                         socket.remoteId = remoteId;
  1299.                         cb(socket)
  1300.                     }
  1301.                     var data = socket.pendingWrite;
  1302.                     if (data) {
  1303.                         cb = socket.wrote;
  1304.                         delete socket.wrote;
  1305.                         delete socket.pendingWrite;
  1306.                         socket.write(data, cb);
  1307.                         return
  1308.                     }
  1309.                     cb = socket.wrote;
  1310.                     if (cb) {
  1311.                         delete socket.wrote;
  1312.                         cb()
  1313.                     }
  1314.                     break;
  1315.                 case AdbDevice.kCommandCNXN:
  1316.                     this.rawProperties = ab2str(payload);
  1317.                     this.properties = parseConnectionPayload(payload);
  1318.                     var cb = this.onConnected;
  1319.                     if (cb) {
  1320.                         delete this.onConnected;
  1321.                         cb(this)
  1322.                     }
  1323.                     break;
  1324.                 case AdbDevice.kCommandWRTE:
  1325.                     var remoteId = arg0;
  1326.                     var localId = arg1;
  1327.                     var socket = this.sockets[localId];
  1328.                     if (!socket) {
  1329.                         this.handleUnknown(localId, remoteId);
  1330.                         return
  1331.                     }
  1332.                     if (!socket.paused) {
  1333.                         this.sendMessage(AdbDevice.kCommandOKAY, socket.localId, socket.remoteId)
  1334.                     }
  1335.                     socket.dataReceived(new Uint8Array(payload));
  1336.                     break;
  1337.                 case AdbDevice.kCommandCLSE:
  1338.                     var remoteId = arg0;
  1339.                     var localId = arg1;
  1340.                     var socket = this.sockets[localId];
  1341.                     if (!socket) {
  1342.                         console.log("asked to close unknown socket?");
  1343.                         return
  1344.                     }
  1345.                     delete this.sockets[localId];
  1346.                     socket.destroy();
  1347.                     var cb = socket.onConnected;
  1348.                     if (cb) {
  1349.                         delete socket.onConnected;
  1350.                         cb()
  1351.                     }
  1352.                     break;
  1353.                 default:
  1354.                     console.log("unknown command: ", command.toString(16), arg0, arg1, payload);
  1355.                     break
  1356.             }
  1357.         }.bind(this);
  1358.         if (!transferLen) {
  1359.             try {
  1360.                 handleMessage(null)
  1361.             } finally {
  1362.                 this.receiveMessages()
  1363.             }
  1364.             return
  1365.         }
  1366.         this.transport.read(transferLen, function(result) {
  1367.             if (result.resultCode) {
  1368.                 this.fatal(result);
  1369.                 return
  1370.             }
  1371.             var payload = result.data;
  1372.             if (AdbDevice.checksum(payload) != header.getUint32(16, true)) {
  1373.                 this.receiveMessages();
  1374.                 return
  1375.             }
  1376.             try {
  1377.                 handleMessage(payload)
  1378.             } finally {
  1379.                 this.receiveMessages()
  1380.             }
  1381.         }.bind(this))
  1382.     }.bind(this))
  1383. };
  1384. AdbDevice.prototype.forwardPort = function(args) {
  1385.     var forwardingServer = new Server;
  1386.     forwardingServer.listen({
  1387.         port: args.fromPort,
  1388.         address: "127.0.0.1"
  1389.     }, function(localSocket) {
  1390.         this.newSocket(args.to, function(remoteSocket) {
  1391.             if (remoteSocket) Socket.stream(localSocket, remoteSocket);
  1392.             else localSocket.destroy()
  1393.         }.bind(this))
  1394.     }.bind(this), function() {
  1395.         this.forwards[args.fromPort] = forwardingServer
  1396.     }.bind(this))
  1397. };
  1398. AdbDevice.prototype.newAdbSocket = function(socketId, cb) {
  1399.     var socket;
  1400.     if (this.createSocket) socket = this.createSocket(socketId, cb);
  1401.     else socket = new AdbSocket(this, socketId, cb);
  1402.     return socket
  1403. };
  1404. AdbDevice.prototype.newSocket = function(service, cb) {
  1405.     var socketId = ++this.currentSocketId;
  1406.     this.sockets[socketId] = this.newAdbSocket(socketId, cb);
  1407.     this.sendMessage(AdbDevice.kCommandOPEN, socketId, 0, service)
  1408. };
  1409.  
  1410. function AdbSocket(device, socketId, cb) {
  1411.     if (!cb) {
  1412.         cb = function() {}
  1413.     }
  1414.     this.device = device;
  1415.     this.localId = socketId;
  1416.     this.onConnected = cb
  1417. }
  1418. AdbSocket.prototype.write = function(data, cb) {
  1419.     if (this.pendingWrite || this.wrote) {
  1420.         console.log("bad adb socket state, already writing");
  1421.         throw new Error("bad adb socket state, already writing");
  1422.     }
  1423.     var toWrite = Math.min(this.device.transport.zero_mask, this.device.maxPayload);
  1424.     if (toWrite < data.byteLength) {
  1425.         this.pendingWrite = data.slice(toWrite);
  1426.         data = data.slice(0, toWrite)
  1427.     } else {
  1428.         this.pendingWrite = null
  1429.     }
  1430.     this.wrote = cb;
  1431.     this.device.sendMessage(AdbDevice.kCommandWRTE, this.localId, this.remoteId, data)
  1432. };
  1433. AdbSocket.prototype.destroy = function() {
  1434.     this.device.sendMessage(AdbDevice.kCommandCLSE, this.localId, this.remoteId);
  1435.     this.dataReceived(null)
  1436. };
  1437. AdbSocket.prototype.buffered = Socket.prototype.buffered;
  1438. AdbSocket.prototype.dataReceived = Socket.prototype.dataReceived;
  1439. AdbSocket.prototype.read = Socket.prototype.read;
  1440. AdbSocket.prototype.pause = Socket.prototype.pause;
  1441. AdbSocket.prototype.resume = Socket.prototype.resume;
  1442. AdbSocket.prototype.unshift = Socket.prototype.unshift;
  1443. AdbSocket.prototype.onPause = function() {};
  1444. AdbSocket.prototype.onResume = function() {
  1445.     this.device.sendMessage(AdbDevice.kCommandOKAY, this.localId, this.remoteId)
  1446. };
  1447.  
  1448. function connectUsbAdb(handle, iface, cb) {
  1449.     console.log("connecting");
  1450.     var adb = new AdbDevice(new AdbUsbTransport(handle, iface), cb);
  1451.     console.log("sending CNXN");
  1452.     adb.sendMessage(AdbDevice.kCommandCNXN, AdbDevice.ADB_PROTOCOL_VERSION, AdbDevice.MAX_PAYLOAD, "host::");
  1453.     console.log("starting receive loop");
  1454.     adb.receiveMessages()
  1455. }
  1456.  
  1457. function AdbServer(options) {
  1458.     var options = options || {};
  1459.     var port = options.port || 5037;
  1460.     var start = options.start !== false;
  1461.     this.currentSocketId = 0;
  1462.     this.pendingDevices = {};
  1463.     this.port = port;
  1464.     this.adbDevices = {};
  1465.     this.clients = {};
  1466.     if (start) {
  1467.         this.start()
  1468.     }
  1469. }
  1470.  
  1471. function _nowMs() {
  1472.     return (new Date).getTime()
  1473. }
  1474. AdbServer.prototype.start = function() {
  1475.     if (this.server) {
  1476.         console.log("ADB Server started while already started");
  1477.         return
  1478.     }
  1479.     this.lastChange = _nowMs();
  1480.     this.clients = {};
  1481.     this.adbDevices = {};
  1482.     this.pendingDevices = {};
  1483.     this.refreshing = {};
  1484.     var server = new Server;
  1485.     server.listen({
  1486.         port: this.port,
  1487.         address: "127.0.0.1"
  1488.     }, function(socket) {
  1489.         var client = new AdbClient(this, socket);
  1490.         var socketId = ++this.currentSocketId;
  1491.         this.clients[socketId] = client;
  1492.         socket.onClose = function() {
  1493.             delete this.clients[socketId]
  1494.         }.bind(this);
  1495.         client.receiveHeader()
  1496.     }.bind(this), function(result) {
  1497.         if (result) {
  1498.             console.log("adb server failed to listen: " + result);
  1499.             return
  1500.         }
  1501.         console.log("ADB Server started");
  1502.         this.server = server;
  1503.         this.refresh()
  1504.     }.bind(this))
  1505. };
  1506. AdbServer.prototype.isRunning = function() {
  1507.     return this.server != null
  1508. };
  1509. AdbServer.prototype.kill = function() {
  1510.     this.lastChange = _nowMs();
  1511.     this.server.destroy();
  1512.     this.server = null;
  1513.     this.refreshing = {};
  1514.     for (var client in this.clients) {
  1515.         client = this.clients[client];
  1516.         client.socket.destroy()
  1517.     }
  1518.     this.clients = {};
  1519.     for (var device in this.adbDevices) {
  1520.         device = this.adbDevices[device];
  1521.         device.destroy()
  1522.     }
  1523.     this.adbDevices = {};
  1524.     this.pendingDevices = {}
  1525. };
  1526. AdbServer.prototype.selectDevice = function(cb) {
  1527.     chrome.usb.getUserSelectedDevices({
  1528.         filters: [{
  1529.             interfaceClass: 255,
  1530.             interfaceSubclass: 66,
  1531.             interfaceProtocol: 1
  1532.         }]
  1533.     }, function(devices) {
  1534.         for (var device in devices) {
  1535.             device = devices[device];
  1536.             this.refreshDevice(device, cb)
  1537.         }
  1538.     }.bind(this))
  1539. };
  1540. AdbServer.prototype.withAdbDevice = function(adb, cb) {
  1541.     adb.onError = function() {
  1542.         this.lastChange = _nowMs();
  1543.         delete this.adbDevices[adb.serialno]
  1544.     }.bind(this);
  1545.     var withSerial = function(str) {
  1546.         this.lastChange = _nowMs();
  1547.         adb.serialno = str.trim();
  1548.         this.adbDevices[adb.serialno] = adb;
  1549.         console.log("found device: " + adb.serialno);
  1550.         cb(adb)
  1551.     }.bind(this);
  1552.     if (adb.serialno) {
  1553.         withSerial(adb.serialno);
  1554.         return
  1555.     }
  1556.     adb.newSocket("shell:getprop ro.serialno", function(adbSocket) {
  1557.         readString(adbSocket, function(str) {
  1558.             withSerial(str)
  1559.         }.bind(this))
  1560.     }.bind(this))
  1561. };
  1562. AdbServer.prototype.tryDevice = function(handle, cb) {
  1563.     var adbDevices = this.adbDevices;
  1564.     var pending = this.pendingDevices;
  1565.     var server = this;
  1566.  
  1567.     function tryInterface(i) {
  1568.         var number = i.interfaceNumber;
  1569.         for (var existing in adbDevices) {
  1570.             existing = adbDevices[existing];
  1571.             if (existing.transport.iface && existing.transport.handle && existing.transport.iface.interfaceNumber == number && existing.transport.handle.productId == handle.productId && existing.transport.handle.vendorId == handle.vendorId) {
  1572.                 return false
  1573.             }
  1574.         }
  1575.         if (pending[number]) {
  1576.             return false
  1577.         }
  1578.         pending[number] = handle;
  1579.         console.log("claiming:", JSON.stringify(handle), JSON.stringify(i));
  1580.         chrome.usb.claimInterface(handle, i.interfaceNumber, function() {
  1581.             console.log("claimed:", JSON.stringify(chrome.runtime.lastError));
  1582.             connectUsbAdb(handle, i, function(adb) {
  1583.                 if (!adb) {
  1584.                     delete pending[number];
  1585.                     cb();
  1586.                     return
  1587.                 }
  1588.                 server.withAdbDevice(adb, function(adb) {
  1589.                     delete pending[number];
  1590.                     cb(adb)
  1591.                 })
  1592.             })
  1593.         });
  1594.         return true
  1595.     }
  1596.     chrome.usb.listInterfaces(handle, unsafeCallback(function(interfaces) {
  1597.         if (!interfaces) {
  1598.             console.log("unable list interfaces", JSON.stringify(chrome.runtime.lastError));
  1599.             if (cb) cb();
  1600.             return
  1601.         }
  1602.         console.log("got interfaces", JSON.stringify(interfaces));
  1603.         var usedHandle = false;
  1604.         for (var i in interfaces) {
  1605.             i = interfaces[i];
  1606.             if (i.interfaceClass == 255 && i.interfaceSubclass == 66 && i.interfaceProtocol == 1) {
  1607.                 usedHandle |= tryInterface(i)
  1608.             }
  1609.         }
  1610.         if (!usedHandle) {
  1611.             chrome.usb.closeDevice(handle)
  1612.         }
  1613.     }))
  1614. };
  1615. AdbServer.prototype.refreshDevice = function(device, cb) {
  1616.     chrome.usb.openDevice(device, function(connectionHandle) {
  1617.         if (!connectionHandle) {
  1618.             console.log("unable to open device", JSON.stringify(chrome.runtime.lastError));
  1619.             if (cb) cb();
  1620.             return
  1621.         }
  1622.         this.start();
  1623.         this.tryDevice(connectionHandle, function(adb) {
  1624.             if (adb) {
  1625.                 adb.usbDevice = device
  1626.             }
  1627.             cb(adb)
  1628.         })
  1629.     }.bind(this))
  1630. };
  1631. AdbServer.prototype.refresh = function() {
  1632.     if (!this.server) {
  1633.         console.log("adb server refresh requested while server killed");
  1634.         return
  1635.     }
  1636.     var now = _nowMs();
  1637.     if (this.server.lastRefresh && this.server.lastRefresh > now - 1e4) {
  1638.         return
  1639.     }
  1640.     this.server.lastRefresh = now;
  1641.     var vidpids = chrome.runtime.getManifest().permissions.pop().usbDevices;
  1642.     $(vidpids).each(function(index, vidpid) {
  1643.         var key = vidpid.vendorId + "&" + vidpid.productId;
  1644.         if (this.refreshing[key]) {
  1645.             return
  1646.         }
  1647.         this.refreshing[key] = true;
  1648.         chrome.usb.findDevices({
  1649.             productId: vidpid.productId,
  1650.             vendorId: vidpid.vendorId
  1651.         }, function(devices) {
  1652.             var waiting = devices.length;
  1653.             if (!waiting) {
  1654.                 delete this.refreshing[key];
  1655.                 return
  1656.             }
  1657.             console.log("found:", vidpid, devices);
  1658.             for (var device in devices) {
  1659.                 console.log("trying:", devices[device]);
  1660.                 this.tryDevice(devices[device], function() {
  1661.                     waiting--;
  1662.                     if (!waiting) {
  1663.                         delete this.refreshing[key]
  1664.                     }
  1665.                 }.bind(this))
  1666.             }
  1667.         }.bind(this))
  1668.     }.bind(this));
  1669.     var now = _nowMs();
  1670.     for (var client in this.clients) {
  1671.         client = this.clients[client];
  1672.         if (client.tracking && now != client.tracking) {
  1673.             client.tracking = now;
  1674.             client.writeDevices({
  1675.                 filter: client.tracked
  1676.             })
  1677.         }
  1678.     }
  1679. };
  1680. AdbServer.prototype.stop = function() {
  1681.     this.server.destroy()
  1682. };
  1683.  
  1684. function AdbClient(server, socket) {
  1685.     this.server = server;
  1686.     this.socket = socket
  1687. }
  1688. AdbClient.prototype.resolveTransport = function(transport, serialno) {
  1689.     if (serialno) {
  1690.         var ret = this.server.adbDevices[serialno];
  1691.         if (!ret) return "device not found";
  1692.         return ret
  1693.     }
  1694.     var num = Object.keys(this.server.adbDevices);
  1695.     if (num > 1) {
  1696.         return "more than one device"
  1697.     }
  1698.     if (num == 0) {
  1699.         return "no devices connected"
  1700.     }
  1701.     for (var device in this.server.adbDevices) {
  1702.         return this.server.adbDevices[device]
  1703.     }
  1704. };
  1705. AdbClient.prototype.write = function(data, status) {
  1706.     if (!status) {
  1707.         status = "OKAY"
  1708.     }
  1709.     data = str2ab(data);
  1710.     var len = data.byteLength;
  1711.     var len16 = make4Len16(len);
  1712.     len16 = str2ab(status + len16);
  1713.     var payload = appendBuffer(new Uint8Array(len16), new Uint8Array(data)).buffer;
  1714.     this.socket.write(payload, function() {})
  1715. };
  1716. AdbClient.prototype.writeDevices = function(options) {
  1717.     var options = options || {};
  1718.     var longformDevices = options.longformDevices;
  1719.     var filter = options.filter || null;
  1720.     var devices = "";
  1721.     for (var device in this.server.adbDevices) {
  1722.         if (filter && filter[device]) continue;
  1723.         device = this.server.adbDevices[device];
  1724.         devices += device.serialno + "  device";
  1725.         if (longformDevices) {
  1726.             if (device.transport.constructor.name == "AdbUsbTransport") devices += " usb:" + device.transport.iface.interfaceNumber;
  1727.             else devices += " tpcip:" + "something";
  1728.             devices += " product:" + device.properties["ro.product.name"];
  1729.             devices += " model:" + device.properties["ro.product.model"];
  1730.             devices += " device:" + device.properties["ro.product.device"]
  1731.         }
  1732.         devices += "\n"
  1733.     }
  1734.     if (filter != null && devices.length == 0) return;
  1735.     this.write(devices)
  1736. };
  1737. AdbClient.prototype.handlePayload = function(data) {
  1738.     data = ab2str(data);
  1739.     var commandParts = data.split(":");
  1740.     var hostCmd = data;
  1741.     var serialno;
  1742.     if (commandParts[0] == "host-serial") {
  1743.         commandParts[0] = "host";
  1744.         serialno = commandParts.splice(1, 1)[0];
  1745.         if (Number.isInteger(parseInt(commandParts[1]))) {
  1746.             serialno += ":" + commandParts.splice(1, 1)[0]
  1747.         }
  1748.     }
  1749.     if (commandParts.length >= 2) hostCmd = commandParts[0] + ":" + commandParts[1];
  1750.     switch (hostCmd) {
  1751.         case "host:version":
  1752.             this.write(make4Len16(AdbDevice.ADB_VERSION));
  1753.             break;
  1754.         case "host:devices-l":
  1755.         case "host:devices":
  1756.             var longformDevices = data == "host:devices-l";
  1757.             this.server.refresh();
  1758.             this.writeDevices({
  1759.                 longformDevices: longformDevices
  1760.             });
  1761.             break;
  1762.         case "host:transport-usb":
  1763.         case "host:transport-any":
  1764.             var transport = this.resolveTransport(data, serialno);
  1765.             if (transport.constructor.name == "String") {
  1766.                 this.write(transport, "FAIL");
  1767.                 break
  1768.             }
  1769.             this.transport = transport;
  1770.             this.socket.write(str2ab("OKAY"), function() {});
  1771.             break;
  1772.         case "host:kill":
  1773.             this.server.kill();
  1774.             break;
  1775.         case "host:connect":
  1776.             if (commandParts.length < 3) {
  1777.                 this.write("need more arguments for connect <host>[:<port>]", "FAIL");
  1778.                 break
  1779.             }
  1780.             var remoteHost = commandParts[2];
  1781.             var remotePort = 5555;
  1782.             if (commandParts.length > 3) remotePort = Number.parseInt(commandParts[3]);
  1783.             Socket.connect({
  1784.                 host: remoteHost,
  1785.                 port: remotePort
  1786.             }, function(socket) {
  1787.                 if (!socket) {
  1788.                     this.write("connecting " + remoteHost + " " + remotePort, "FAIL");
  1789.                     return this
  1790.                 }
  1791.                 var adb = new AdbDevice(new AdbTcpTransport(socket), function(adb) {
  1792.                     this.server.withAdbDevice(adb, function() {
  1793.                         console.log("connected?");
  1794.                         this.socket.write(str2ab("OKAYOKAY"), function() {})
  1795.                     }.bind(this))
  1796.                 }.bind(this));
  1797.                 adb.serialno = remoteHost + ":" + remotePort;
  1798.                 socket.onClose = function() {
  1799.                     adb.fatal("socket closed")
  1800.                 }.bind(this);
  1801.                 adb.sendMessage(AdbDevice.kCommandCNXN, AdbDevice.ADB_PROTOCOL_VERSION, AdbDevice.MAX_PAYLOAD, "host::");
  1802.                 adb.receiveMessages()
  1803.             }.bind(this));
  1804.             break;
  1805.         case "host:track-devices":
  1806.             this.tracking = _nowMs();
  1807.             this.writeDevices();
  1808.             this.tracked = Object.fromArray(Object.keys(this.server.adbDevices));
  1809.             break;
  1810.         case "host:forward":
  1811.             var forwardParts = commandParts.join(":").substring(hostCmd.length + 1).split(";");
  1812.             var from = forwardParts[0].split(":");
  1813.             var fromPort = parseInt(from[1]);
  1814.             var transport = this.resolveTransport(data, serialno);
  1815.             if (transport.constructor.name == "String") {
  1816.                 this.write(transport, "FAIL");
  1817.                 break
  1818.             }
  1819.             transport.forwardPort({
  1820.                 fromPort: fromPort,
  1821.                 to: forwardParts[1]
  1822.             });
  1823.             this.socket.write(str2ab("OKAYOKAY"), function() {}.bind(this));
  1824.             break;
  1825.         default:
  1826.             if (this.transport) {
  1827.                 var transport = this.transport;
  1828.                 transport.newSocket(data, function(socket) {
  1829.                     this.socket.write(str2ab("OKAY"), function() {});
  1830.                     Socket.stream(socket, this.socket)
  1831.                 }.bind(this));
  1832.                 return
  1833.             }
  1834.             var specific = "host:transport:";
  1835.             if (data.startsWith(specific)) {
  1836.                 var serialno = data.substr(specific.length);
  1837.                 var device = this.server.adbDevices[serialno];
  1838.                 if (!device) {
  1839.                     this.write("device not found", "FAIL");
  1840.                     return
  1841.                 }
  1842.                 this.transport = device;
  1843.                 this.socket.write(str2ab("OKAY"), function() {});
  1844.                 break
  1845.             }
  1846.             console.log("unknown request: " + data);
  1847.             this.write("unknown command: " + data, "FAIL");
  1848.             var appName = chrome.runtime.getManifest().name;
  1849.             chrome.notifications.create({
  1850.                 type: "basic",
  1851.                 iconUrl: "/icon.png",
  1852.                 title: appName,
  1853.                 message: appName + "'s adb server encountered an unknown adb command.\nYou may want to close " + appName + " and start your adb binary manually."
  1854.             });
  1855.             break
  1856.     }
  1857.     this.receiveHeader()
  1858. };
  1859. AdbClient.prototype.receivePayload = function(data) {
  1860.     var len = parseInt(ab2str(data), 16);
  1861.     this.socket.read(len, this.handlePayload.bind(this))
  1862. };
  1863. AdbClient.prototype.receiveHeader = function() {
  1864.     this.socket.read(4, this.receivePayload.bind(this))
  1865. };
  1866. var Adb = {};
  1867. Adb.sendHostCommand = function(command, cb) {
  1868.     Socket.connect({
  1869.         host: "127.0.0.1",
  1870.         port: 5037
  1871.     }, function(socket) {
  1872.         if (!socket) {
  1873.             cb();
  1874.             return
  1875.         }
  1876.         command = make4Len16(command.length) + command;
  1877.         socket.read(4, function(ok) {
  1878.             if (ab2str(ok) != "OKAY") {
  1879.                 socket.destroy();
  1880.                 cb();
  1881.                 return
  1882.             }
  1883.             socket.read(4, function(len) {
  1884.                 var lenStr = ab2str(len);
  1885.                 len = parseInt(lenStr, 16);
  1886.                 if (len == 0 || lenStr == "OKAY") {
  1887.                     cb(socket, new ArrayBuffer(0));
  1888.                     return
  1889.                 }
  1890.                 socket.read(len, function(data) {
  1891.                     cb(socket, data)
  1892.                 })
  1893.             })
  1894.         });
  1895.         socket.write(str2ab(command), function() {})
  1896.     })
  1897. };
  1898. Adb.devices = function(cb) {
  1899.     var adbDevices = {};
  1900.  
  1901.     function parseConnectionPayload(payload) {
  1902.         var rawPayload = payload;
  1903.         payload = payload.replace(" ", " ");
  1904.         var i = payload.indexOf(" ");
  1905.         if (i == -1) {
  1906.             cb({});
  1907.             return
  1908.         }
  1909.         var serialno = payload.substring(0, i);
  1910.         payload = payload.substring(i).trim();
  1911.         var newPayload;
  1912.         while (newPayload != payload) {
  1913.             newPayload = payload;
  1914.             payload = payload.replace("  ", " ")
  1915.         }
  1916.         var values = {};
  1917.         var firstSpace = payload.indexOf(" ");
  1918.         if (firstSpace == -1) return;
  1919.         var status = payload.substring(0, firstSpace);
  1920.         payload = payload.substring(firstSpace + 1);
  1921.         while (payload.length) {
  1922.             i = payload.indexOf(":");
  1923.             if (i == -1) break;
  1924.             var key = payload.substring(0, i);
  1925.             var rest = payload.substring(i + 1);
  1926.             var nextSpace = rest.indexOf(" ");
  1927.             var nextColon = rest.indexOf(":");
  1928.             var value;
  1929.             if (nextSpace == -1 || nextColon == -1) {
  1930.                 value = rest;
  1931.                 payload = ""
  1932.             } else {
  1933.                 while (nextSpace != -1 && nextSpace < nextColon) {
  1934.                     value = rest.substring(0, nextSpace);
  1935.                     payload = rest.substring(nextSpace + 1);
  1936.                     nextSpace = rest.indexOf(" ", nextSpace + 1)
  1937.                 }
  1938.             }
  1939.             values[key] = value
  1940.         }
  1941.         var name;
  1942.         if (!values["model"]) name = serialno;
  1943.         else name = values["model"].replace("_", " ");
  1944.         adbDevices[serialno] = {
  1945.             name: name,
  1946.             status: status,
  1947.             properties: rawPayload
  1948.         }
  1949.     }
  1950.     Adb.sendHostCommand("host:devices-l", function(socket, data) {
  1951.         if (!socket) {
  1952.             cb();
  1953.             return
  1954.         }
  1955.         socket.destroy();
  1956.         data = ab2str(data);
  1957.         data = data.trim();
  1958.         var lines = data.split("\n");
  1959.         for (var line in lines) {
  1960.             line = lines[line];
  1961.             parseConnectionPayload(line)
  1962.         }
  1963.         cb(adbDevices)
  1964.     })
  1965. };
  1966. Adb.killServer = function(cb) {
  1967.     Adb.sendHostCommand("host:kill-server", function(socket, data) {
  1968.         if (!socket) {
  1969.             cb();
  1970.             return
  1971.         }
  1972.         socket.destroy();
  1973.         data = ab2str(data);
  1974.         if (cb) cb()
  1975.     })
  1976. };
  1977. Adb.sendClientCommand = function(options, cb) {
  1978.     var command = options.command;
  1979.     var serialno = options.serialno;
  1980.     Socket.connect({
  1981.         host: "127.0.0.1",
  1982.         port: 5037
  1983.     }, function(socket) {
  1984.         if (!socket) {
  1985.             cb();
  1986.             return
  1987.         }
  1988.         socket.read(4, function(data) {
  1989.             var result = ab2str(data);
  1990.             if (result != "OKAY") {
  1991.                 socket.destroy();
  1992.                 cb(null);
  1993.                 return
  1994.             }
  1995.             var clientCommand = command;
  1996.             clientCommand = make4Len16(clientCommand.length) + clientCommand;
  1997.             socket.read(4, function(data) {
  1998.                 var result = ab2str(data);
  1999.                 if (result != "OKAY") {
  2000.                     socket.destroy();
  2001.                     cb(null);
  2002.                     return
  2003.                 }
  2004.                 cb(socket)
  2005.             });
  2006.             socket.write(str2ab(clientCommand), function() {})
  2007.         });
  2008.         var hostCommand = "host:transport:" + serialno;
  2009.         hostCommand = make4Len16(hostCommand.length) + hostCommand;
  2010.         socket.write(str2ab(hostCommand), function() {})
  2011.     })
  2012. };
  2013. Adb.shell = function(options, cb) {
  2014.     var command = options.command;
  2015.     var serialno = options.serialno;
  2016.     Adb.sendClientCommand({
  2017.         serialno: serialno,
  2018.         command: "shell:" + command
  2019.     }, function(socket) {
  2020.         if (!socket) {
  2021.             cb();
  2022.             return
  2023.         }
  2024.         readString(socket, function(str) {
  2025.             cb(str)
  2026.         })
  2027.     })
  2028. };
  2029. Adb.forward = function(options, cb) {
  2030.     var command = "host-serial:" + options.serialno + ":forward:" + options.from + ";" + options.to;
  2031.     Adb.sendHostCommand(command, function(socket, err) {
  2032.         if (socket) socket.destroy();
  2033.         cb(socket, err)
  2034.     })
  2035. };
  2036.  
  2037. function AdbSync() {}
  2038. AdbSync.MKID = function(a, b, c, d) {
  2039.     return a.charCodeAt(0) | b.charCodeAt(0) << 8 | c.charCodeAt(0) << 16 | d.charCodeAt(0) << 24
  2040. };
  2041. AdbSync.ID_RECV = AdbSync.MKID("R", "E", "C", "V");
  2042. AdbSync.ID_SEND = AdbSync.MKID("S", "E", "N", "D");
  2043. AdbSync.ID_DONE = AdbSync.MKID("D", "O", "N", "E");
  2044. AdbSync.ID_DATA = AdbSync.MKID("D", "A", "T", "A");
  2045. AdbSync.DATA_MAX = 64 * 1024;
  2046. Adb.pull = function(options, cb) {
  2047.     var file = options.file;
  2048.     var serialno = options.serialno;
  2049.     var fileEntry = options.fileEntry;
  2050.     Adb.sendClientCommand({
  2051.         serialno: serialno,
  2052.         command: "sync:"
  2053.     }, function(socket) {
  2054.         if (!socket) {
  2055.             cb();
  2056.             return
  2057.         }
  2058.         fileEntry.createWriter(function(fileWriter) {
  2059.             var msg = new ArrayBuffer(8);
  2060.             var msgView = new DataView(msg);
  2061.             msgView.setUint32(0, AdbSync.ID_RECV, true);
  2062.             msgView.setUint32(4, file.length, true);
  2063.             socket.write(msg, function() {
  2064.                 socket.write(str2ab(file), function() {
  2065.                     function readChunk(len) {
  2066.                         socket.read(len, function(data) {
  2067.                             fileWriter.write(new Blob([data]))
  2068.                         })
  2069.                     }
  2070.                     fileWriter.onwriteend = function(e) {
  2071.                         readHeader()
  2072.                     };
  2073.  
  2074.                     function readHeader() {
  2075.                         socket.read(8, function(data) {
  2076.                             var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
  2077.                             var id = view.getUint32(0, true);
  2078.                             if (id == AdbSync.ID_DATA) {
  2079.                                 var len = view.getUint32(4, true);
  2080.                                 readChunk(len);
  2081.                                 return
  2082.                             }
  2083.                             socket.destroy();
  2084.                             if (id == AdbSync.ID_DONE) {
  2085.                                 cb(fileEntry);
  2086.                                 return
  2087.                             }
  2088.                             cb()
  2089.                         })
  2090.                     }
  2091.                     readHeader()
  2092.                 })
  2093.             })
  2094.         })
  2095.     })
  2096. };
  2097. Adb.push = function(options, cb) {
  2098.     var file = options.file;
  2099.     var serialno = options.serialno;
  2100.     var fileSocket = options.socket;
  2101.     Adb.sendClientCommand({
  2102.         serialno: serialno,
  2103.         command: "sync:"
  2104.     }, function(socket) {
  2105.         if (!socket) {
  2106.             cb();
  2107.             return
  2108.         }
  2109.         var msg = new ArrayBuffer(8);
  2110.         var msgView = new DataView(msg);
  2111.         var fileAndMode = file + ",0644";
  2112.         msgView.setUint32(0, AdbSync.ID_SEND, true);
  2113.         msgView.setUint32(4, fileAndMode.length, true);
  2114.         socket.write(msg, function() {
  2115.             socket.write(str2ab(fileAndMode), function() {
  2116.                 var done;
  2117.                 var writing = true;
  2118.                 fileSocket.onClose = function() {
  2119.                     var msg = new ArrayBuffer(8);
  2120.                     var msgView = new DataView(msg);
  2121.                     msgView.setUint32(0, AdbSync.ID_DONE, true);
  2122.                     msgView.setUint32(4, 0, true);
  2123.                     socket.write(msg, function() {
  2124.                         socket.read(8, function() {
  2125.                             cb()
  2126.                         })
  2127.                     })
  2128.                 };
  2129.  
  2130.                 function readChunk() {
  2131.                     fileSocket.read(function(data) {
  2132.                         if (data.byteLength > AdbSync.DATA_MAX) {
  2133.                             var extra = data.subarray(AdbSync.DATA_MAX);
  2134.                             data = data.subarray(0, AdbSync.DATA_MAX);
  2135.                             fileSocket.unshift(extra)
  2136.                         }
  2137.                         writeChunk(data)
  2138.                     })
  2139.                 }
  2140.  
  2141.                 function writeChunk(data) {
  2142.                     var msg = new ArrayBuffer(8);
  2143.                     var msgView = new DataView(msg);
  2144.                     msgView.setUint32(0, AdbSync.ID_DATA, true);
  2145.                     msgView.setUint32(4, data.byteLength, true);
  2146.                     socket.write(msg, function() {
  2147.                         var buffer = data.buffer;
  2148.                         if (data.byteOffset || data.length != buffer.byteLength) {
  2149.                             buffer = buffer.slice(data.byteOffset, data.byteOffset + data.byteLength)
  2150.                         }
  2151.                         socket.write(buffer, function() {
  2152.                             readChunk()
  2153.                         })
  2154.                     })
  2155.                 }
  2156.                 readChunk()
  2157.             })
  2158.         })
  2159.     })
  2160. };
  2161.  
  2162. function AdbDaemon(transport, maxPayload) {
  2163.     this.transport = transport;
  2164.     this.sockets = {};
  2165.     this.currentSocketId = 0;
  2166.     this.maxPayload = maxPayload || AdbDevice.MAX_PAYLOAD
  2167. }
  2168. AdbDaemon.prototype.start = function(properties) {
  2169.     var props = str2ab(properties, undefined, true);
  2170.     this.sendMessage(AdbDevice.kCommandCNXN, AdbDevice.ADB_PROTOCOL_VERSION, this.maxPayload, props);
  2171.     this.receiveMessages()
  2172. };
  2173. AdbDaemon.prototype.fatal = function(result) {
  2174.     console.log("fatal error", result)
  2175. };
  2176. AdbDaemon.prototype.sendMessage = AdbDevice.prototype.sendMessage;
  2177. AdbDaemon.prototype.receiveMessages = AdbDevice.prototype.receiveMessages;
  2178. AdbDaemon.prototype.handleUnknown = AdbDevice.prototype.handleUnknown;
  2179. AdbDaemon.prototype.newAdbSocket = AdbDevice.prototype.newAdbSocket;
  2180. AdbDaemon.prototype.destroy = AdbDevice.prototype.destroy;
  2181. AdbDaemon.prototype.onOpenSocket = function(payload, remoteSocketId) {
  2182.     if (this.openSocket) {
  2183.         var socketId = ++this.currentSocketId;
  2184.         var socket = this.newAdbSocket(socketId);
  2185.         socket.remoteId = remoteSocketId;
  2186.         this.sockets[socketId] = socket;
  2187.         this.sendMessage(AdbDevice.kCommandOKAY, socketId, remoteSocketId);
  2188.         this.openSocket(ab2str(payload), socket)
  2189.     }
  2190. };
  2191.  
  2192. function LicenseManager() {
  2193.     this.licensed = false;
  2194.     this.licenseCached = false
  2195. }
  2196. LicenseManager.prototype.refresh = function(cb) {
  2197.     var doCallback = function() {
  2198.         if (cb) cb();
  2199.         if (this.globalRefresh) this.globalRefresh()
  2200.     }.bind(this);
  2201.     var checkPayments = function(payments) {
  2202.         $.each(payments, function(index, detail) {
  2203.             console.log("subscription status", detail.sku, detail.state);
  2204.             if (detail.state == "ACTIVE") this.licensed = true
  2205.         }.bind(this))
  2206.     }.bind(this);
  2207.     var checkServer = function() {
  2208.         google.payments.inapp.getPurchases({
  2209.             parameters: {
  2210.                 env: "prod"
  2211.             },
  2212.             success: function(response) {
  2213.                 checkPayments(response.response.details);
  2214.                 if (this.isLicensed()) {
  2215.                     var pw = chrome.app.window.get("purchase");
  2216.                     if (pw != null) {
  2217.                         pw.close();
  2218.                         showNotification("Vysor subscription is active. Thank you for your support!")
  2219.                     }
  2220.                     this.cacheLicense(function() {
  2221.                         if (this.isLicenseCached() && this.globalRefresh) this.globalRefresh()
  2222.                     }.bind(this), {
  2223.                         interactive: false
  2224.                     })
  2225.                 }
  2226.                 doCallback()
  2227.             }.bind(this),
  2228.             failure: function() {
  2229.                 console.log("failed to refresh license", arguments);
  2230.                 doCallback()
  2231.             }.bind(this)
  2232.         })
  2233.     }.bind(this);
  2234.     chrome.storage.local.get("cachedLicense", function(d) {
  2235.         if (!d.cachedLicense) {
  2236.             checkServer();
  2237.             return
  2238.         }
  2239.         var signature = base64ToArrayBuffer(d.cachedLicense.signature);
  2240.         var signedData = str2ab(d.cachedLicense.signed_data);
  2241.         window.crypto.subtle.importKey("jwk", {
  2242.             kty: "RSA",
  2243.             e: "AQAB",
  2244.             n: "vMGBBmLcMO4lOmg-YAHq2DjZKHTaW-xs9KPNXU_zKJ7ZhFhWH3I6skF9ZO8lKeXOSwVEIW4HVMa7m16S6WTrUw",
  2245.             alg: "RS1"
  2246.         }, {
  2247.             name: "RSASSA-PKCS1-v1_5",
  2248.             hash: {
  2249.                 name: "SHA-1"
  2250.             }
  2251.         }, true, ["verify"]).then(function(publicKey) {
  2252.             window.crypto.subtle.verify({
  2253.                 name: "RSASSA-PKCS1-v1_5",
  2254.                 hash: {
  2255.                     name: "SHA-1"
  2256.                 }
  2257.             }, publicKey, signature, signedData).then(function(isvalid) {
  2258.                 if (!isvalid) {
  2259.                     console.error("invalid signature");
  2260.                     checkServer();
  2261.                     return
  2262.                 }
  2263.                 var data = JSON.parse(d.cachedLicense.signed_data);
  2264.                 if (data.date > Date.now()) {
  2265.                     console.log("cached license date from future?");
  2266.                     checkServer();
  2267.                     return
  2268.                 }
  2269.                 if (data.date + 24 * 60 * 60 * 1e3 < Date.now()) {
  2270.                     console.log("cached license is expired.");
  2271.                     checkServer();
  2272.                     return
  2273.                 }
  2274.                 chrome.identity.getProfileUserInfo(function(userInfo) {
  2275.                     if (!userInfo) {
  2276.                         console.log("unable to retrieve user info");
  2277.                         checkServer();
  2278.                         return
  2279.                     }
  2280.                     if (userInfo.id != data.userinfo.id) {
  2281.                         console.log("id mismatch");
  2282.                         checkServer();
  2283.                         return
  2284.                     }
  2285.                     checkPayments(data.payments);
  2286.                     if (!this.isLicensed()) {
  2287.                         checkServer();
  2288.                         return
  2289.                     }
  2290.                     console.log("cached license is valid for " + (data.date + 24 * 60 * 60 * 1e3 - Date.now()) / (60 * 60 * 1e3) + " hours");
  2291.                     this.licenseCached = true;
  2292.                     doCallback()
  2293.                 }.bind(this))
  2294.             }.bind(this)).catch(function(err) {
  2295.                 console.error("cached license check failed", err);
  2296.                 checkServer()
  2297.             })
  2298.         }.bind(this)).catch(function(err) {
  2299.             console.error("key import failed", err);
  2300.             checkServer()
  2301.         })
  2302.     }.bind(this))
  2303. };
  2304. LicenseManager.prototype.cacheLicense = function(cb, opts) {
  2305.     if (!opts) {
  2306.         opts = {
  2307.             interactive: true
  2308.         }
  2309.     }
  2310.     chrome.identity.getAuthToken(opts, function(token) {
  2311.         if (!token) {
  2312.             showNotification("Unable to get auth token.");
  2313.             return
  2314.         }
  2315.         $.ajax({
  2316.             type: "post",
  2317.             url: "https://clockworkbilling.appspot.com/api/v1/verify/google/koushd@gmail.com",
  2318.             data: {
  2319.                 token: token,
  2320.                 item: chrome.runtime.id
  2321.             },
  2322.             dataType: "json",
  2323.             success: function(data) {
  2324.                 this.licenseCached = true;
  2325.                 chrome.storage.local.set({
  2326.                     cachedLicense: data
  2327.                 }, cb)
  2328.             }.bind(this),
  2329.             error: function(xhr, e) {
  2330.                 if (cb) cb(e)
  2331.             }
  2332.         })
  2333.     }.bind(this))
  2334. };
  2335. LicenseManager.prototype.isLicensed = function() {
  2336.     return this.licensed
  2337. };
  2338. LicenseManager.prototype.isLicenseCached = function() {
  2339.     return this.licenseCached
  2340. };
  2341. LicenseManager.prototype.startPurchase = function() {
  2342.     chrome.app.window.create("purchase.html", {
  2343.         id: "purchase",
  2344.         resizable: false,
  2345.         bounds: {
  2346.             width: 800,
  2347.             height: 800
  2348.         }
  2349.     }, function(w) {
  2350.         this.refresh();
  2351.         w.contentWindow.refreshLicenseManager = function() {
  2352.             this.refresh()
  2353.         }.bind(this)
  2354.     }.bind(this))
  2355. };
  2356. (function() {
  2357.     var adbServer = new AdbServer({
  2358.         start: false
  2359.     });
  2360.     var vysorVirtualDevices = {};
  2361.     var shells = {};
  2362.     var list;
  2363.     var shareAdbMaxPayload = 512 * 1024;
  2364.     var analyticsService = analytics.getService("vysor_app");
  2365.     var tracker = analyticsService.getTracker("UA-4956323-6");
  2366.     var licenseManager = new LicenseManager;
  2367.     licenseManager.globalRefresh = function() {
  2368.         refreshListBitrate();
  2369.         refreshListWithLicense()
  2370.     };
  2371.     licenseManager.refresh();
  2372.  
  2373.     function refreshListWithLicense() {
  2374.         if (!licenseManager.isLicensed() || !list) return;
  2375.         $(list.contentWindow.document).find("#purchase").hide();
  2376.         if (licenseManager.isLicenseCached()) $(list.contentWindow.document).find("#login-container").hide();
  2377.         else $(list.contentWindow.document).find("#login-container").show()
  2378.     }
  2379.     var h264Acceleration = "software";
  2380.  
  2381.     function refreshListBitrate() {
  2382.         if (!licenseManager.isLicensed()) return;
  2383.         if (!list) return;
  2384.         chrome.storage.local.get("bitrate", function(d) {
  2385.             $(list.contentWindow.document).find("#bitrate")[0].selectedIndex = d.bitrate
  2386.         })
  2387.     }
  2388.  
  2389.     function libind(feature, f, fb) {
  2390.         return function() {
  2391.             if (!licenseManager.isLicensed()) {
  2392.                 showNotification("The " + feature + " feature is only avaiable to Vysor Pro users.");
  2393.                 licenseManager.startPurchase();
  2394.                 if (fb) fb.apply(this, arguments);
  2395.                 return
  2396.             }
  2397.             f.apply(this, arguments)
  2398.         }
  2399.     }
  2400.  
  2401.     function licheck(feature, f) {
  2402.         if (!licenseManager.isLicensed()) {
  2403.             showNotification("The " + feature + " feature is only avaiable to Vysor Pro users.");
  2404.             licenseManager.startPurchase();
  2405.             return
  2406.         }
  2407.         f()
  2408.     }
  2409.     var vysorForceSocket;
  2410.     chrome.storage.local.get("vysorForceSocket", function(d) {
  2411.         vysorForceSocket = d.vysorForceSocket
  2412.     });
  2413.  
  2414.     function updateWindowStatusText(serialno, text) {
  2415.         var existing = chrome.app.window.get(serialno);
  2416.         if (!existing) return;
  2417.         $(existing.contentWindow.document).find("#loading-text").html(text)
  2418.     }
  2419.  
  2420.     function installApk(serialno, cb) {
  2421.         updateWindowStatusText(serialno, "Installing Vysor APK...");
  2422.         $.ajax({
  2423.             url: "/Vysor-release.apk",
  2424.             dataType: "binary",
  2425.             responseType: "arraybuffer",
  2426.             success: function(ab) {
  2427.                 var uib = new Uint8Array(ab);
  2428.                 var dummy = new DummySocket(uib);
  2429.                 var tmp = "/data/local/tmp/vysor" + (new Date).getTime() + ".apk";
  2430.                 Adb.push({
  2431.                     serialno: serialno,
  2432.                     file: tmp,
  2433.                     socket: dummy
  2434.                 }, function() {
  2435.                     Adb.shell({
  2436.                         command: "pm install -r " + tmp,
  2437.                         serialno: serialno
  2438.                     }, cb)
  2439.                 })
  2440.             }
  2441.         })
  2442.     }
  2443.  
  2444.     function startMirrorServer(serialno, port, cb) {
  2445.         updateWindowStatusText(serialno, "Connecting...");
  2446.  
  2447.         function runWithPath(path) {
  2448.             var password = Math.round(Math.random() * (1 << 30)).toString(16);
  2449.             var pwcmd = "echo -n " + password + " > /data/local/tmp/vysor.pwd ; chmod 600 /data/local/tmp/vysor.pwd";
  2450.             Adb.shell({
  2451.                 command: "ls -l /system/bin/app_process*",
  2452.                 serialno: serialno
  2453.             }, function(test) {
  2454.                 var appProcess = "/system/bin/app_process";
  2455.                 if (test && test.indexOf("app_process32") != -1) {
  2456.                     appProcess += "32"
  2457.                 }
  2458.                 Adb.sendClientCommand({
  2459.                     command: 'shell:sh -c "CLASSPATH=' + path + " " + appProcess + " /system/bin com.koushikdutta.vysor.Main " + password + '"',
  2460.                     serialno: serialno
  2461.                 }, function(socket) {
  2462.                     Adb.shell({
  2463.                         serialno: serialno,
  2464.                         command: 'sh -c "' + pwcmd + '"'
  2465.                     }, function(ignored) {
  2466.                         Socket.eat(socket);
  2467.                         cb(port, password)
  2468.                     })
  2469.                 })
  2470.             })
  2471.         }
  2472.  
  2473.         function tryGetPath(cb) {
  2474.             updateWindowStatusText(serialno, "Connecting...");
  2475.             Adb.shell({
  2476.                 command: "pm path com.koushikdutta.vysor",
  2477.                 serialno: serialno
  2478.             }, function(path) {
  2479.                 if (path == "" || !path) {
  2480.                     cb(null);
  2481.                     return
  2482.                 }
  2483.                 var match = path.match(/package:\/.*?[\r\n]/);
  2484.                 if (!match || !match.length) {
  2485.                     cb(null);
  2486.                     return
  2487.                 }
  2488.                 path = match[0];
  2489.                 path = path.replace("package:", "").trim();
  2490.                 Adb.shell({
  2491.                     command: 'sh -c "CLASSPATH=' + path + ' /system/bin/app_process /system/bin com.koushikdutta.vysor.ProtocolVersionMain"',
  2492.                     serialno: serialno
  2493.                 }, function(version) {
  2494.                     var match = version.match(/vysor-io-.*?[\r\n]/);
  2495.                     if (!match || !match.length) {
  2496.                         cb(null);
  2497.                         return
  2498.                     }
  2499.                     version = match[0];
  2500.                     if (version) version = version.trim();
  2501.                     console.log("protocol version: " + version);
  2502.                     if (version != "vysor-io-18") cb(null);
  2503.                     else cb(path)
  2504.                 })
  2505.             })
  2506.         }
  2507.         tryGetPath(function(path) {
  2508.             if (!path) {
  2509.                 console.log("installing apk");
  2510.                 installApk(serialno, function(result) {
  2511.                     tryGetPath(function(path) {
  2512.                         if (!path) {
  2513.                             console.log("wtf apk install failed? " + result);
  2514.                             showNotification("Error installing APK:\n" + result.trim());
  2515.                             closeWindow(serialno);
  2516.                             return
  2517.                         }
  2518.                         runWithPath(path)
  2519.                     })
  2520.                 });
  2521.                 return
  2522.             }
  2523.             runWithPath(path)
  2524.         })
  2525.     }
  2526.  
  2527.     function closeWindow(serialno) {
  2528.         var existing = chrome.app.window.get(serialno);
  2529.         if (existing) existing.close()
  2530.     }
  2531.     var portCount = 53516;
  2532.  
  2533.     function forwardPort(serialno, cb) {
  2534.         var port = portCount++;
  2535.         Adb.forward({
  2536.             from: "tcp:" + port,
  2537.             to: "tcp:53516",
  2538.             serialno: serialno
  2539.         }, function() {
  2540.             startMirrorServer(serialno, port, cb)
  2541.         })
  2542.     }
  2543.  
  2544.     function withWindow(w, serialno, port, password) {
  2545.         w.contentWindow.adbServer = adbServer;
  2546.         w.contentWindow.Adb = Adb;
  2547.         w.contentWindow.port = port;
  2548.         w.contentWindow.password = password;
  2549.         w.contentWindow.serialno = serialno;
  2550.         w.contentWindow.tracker = tracker;
  2551.         w.contentWindow.vysorVirtualDevices = vysorVirtualDevices;
  2552.         w.contentWindow.vysorForceSocket = vysorForceSocket;
  2553.         w.contentWindow.acceleration = h264Acceleration;
  2554.         w.contentWindow.Function.prototype.libind = function(feature, t) {
  2555.             return function() {
  2556.                 if (!licenseManager.isLicensed()) {
  2557.                     showNotification("The " + feature + " is only avaiable to Vysor Pro users.");
  2558.                     licenseManager.startPurchase();
  2559.                     return
  2560.                 }
  2561.                 this.apply(t, arguments)
  2562.             }.bind(this)
  2563.         };
  2564.         if (!w.contentWindow.hasDocReadied) {
  2565.             $(w.contentWindow.document).ready(function() {
  2566.                 w.contentWindow.hasDocReadied = true;
  2567.                 w.contentWindow.docReady();
  2568.                 w.contentWindow.connectionReady()
  2569.             })
  2570.         } else {
  2571.             w.contentWindow.connectionReady()
  2572.         }
  2573.     }
  2574.  
  2575.     function maybeClose() {
  2576.         setTimeout(function() {
  2577.             if (!chrome.app.window.getAll().length) {
  2578.                 chrome.runtime.reload()
  2579.             }
  2580.         }, 5e3)
  2581.     }
  2582.  
  2583.     function openWindow(serialno, reconnect) {
  2584.         checkForUpdates();
  2585.         var existing = chrome.app.window.get(serialno);
  2586.         if (existing) {
  2587.             existing.show();
  2588.             if (reconnect) {
  2589.                 forwardPort(serialno, function(port, password) {
  2590.                     withWindow(existing, serialno, port, password)
  2591.                 })
  2592.             }
  2593.             return
  2594.         }
  2595.         chrome.app.window.create("screen.html", {
  2596.             id: serialno,
  2597.             bounds: {
  2598.                 width: 405,
  2599.                 height: 720
  2600.             }
  2601.         }, function(w) {
  2602.             forwardPort(serialno, function(port, password) {
  2603.                 withWindow(w, serialno, port, password);
  2604.                 Adb.shell({
  2605.                     command: "am start com.koushikdutta.vysor/.TipsActivity",
  2606.                     serialno: serialno
  2607.                 }, function() {})
  2608.             });
  2609.             w.onClosed.addListener(function() {
  2610.                 maybeClose();
  2611.                 if (w.contentWindow.h264Socket) {
  2612.                     console.log("cleaning up h264 socket");
  2613.                     w.contentWindow.h264Socket.destroy()
  2614.                 }
  2615.             })
  2616.         })
  2617.     }
  2618.  
  2619.     function updateBitrates(index) {
  2620.         var bitrates = [5e5, 75e4, 1e6, 15e5, 2e6];
  2621.         var bitrate = bitrates[index];
  2622.         var windows = chrome.app.window.getAll();
  2623.         for (var w in windows) {
  2624.             w = windows[w];
  2625.             if (!w.contentWindow.serialno) continue;
  2626.             w.contentWindow.sendEvent({
  2627.                 type: "bitrate",
  2628.                 bitrate: bitrate
  2629.             })
  2630.         }
  2631.     }
  2632.  
  2633.     function openList(cb) {
  2634.         if (list) {
  2635.             if (cb) {
  2636.                 cb(list)
  2637.             }
  2638.             return
  2639.         }
  2640.         chrome.app.window.create("list.html", {
  2641.             id: "list",
  2642.             bounds: {
  2643.                 width: 480,
  2644.                 height: 480
  2645.             }
  2646.         }, function(w) {
  2647.             list = w;
  2648.             list.contentWindow.appcallback = function() {
  2649.                 refreshListBitrate();
  2650.                 if (navigator.platform.toLowerCase().indexOf("win") == -1) {
  2651.                     $(list.contentWindow.document).find("#windows").hide()
  2652.                 }
  2653.                 list.contentWindow.adbServer = adbServer;
  2654.                 list.contentWindow.tracker = tracker;
  2655.                 chrome.storage.local.get("connect-automatically", function(vals) {
  2656.                     var connectAuto = vals["connect-automatically"] !== false;
  2657.                     $(list.contentWindow.document).find("#connect-automatically-check").prop("checked", connectAuto)
  2658.                 });
  2659.                 $(list.contentWindow.document).find("#bitrate").change(libind("Image Quality", function() {
  2660.                     updateBitrates(this.selectedIndex);
  2661.                     chrome.storage.local.set({
  2662.                         bitrate: this.selectedIndex
  2663.                     })
  2664.                 }, function() {
  2665.                     this.selectedIndex = 0
  2666.                 }));
  2667.                 $(list.contentWindow.document).find("#connect-automatically-check").change(function() {
  2668.                     chrome.storage.local.set({
  2669.                         "connect-automatically": this.checked
  2670.                     })
  2671.                 });
  2672.                 $(list.contentWindow.document).find("#connect-android").hide();
  2673.                 $(list.contentWindow.document).find("#purchase").click(function() {
  2674.                     licenseManager.startPurchase()
  2675.                 });
  2676.                 $(list.contentWindow.document).find("#login").click(function() {
  2677.                     $(list.contentWindow.document).find("#login-line").hide();
  2678.                     $(list.contentWindow.document).find("#logging-in").show();
  2679.                     licenseManager.cacheLicense(function(e) {
  2680.                         if (e) {
  2681.                             showNotification("Error saving license for offline use: " + e);
  2682.                             $(list.contentWindow.document).find("#login-line").show();
  2683.                             $(list.contentWindow.document).find("#logging-in").hide();
  2684.                             return
  2685.                         }
  2686.                         $(list.contentWindow.document).find("#logging-in").hide();
  2687.                         showNotification("License was saved for offline use. Thanks!")
  2688.                     })
  2689.                 });
  2690.                 refreshListWithLicense()
  2691.             };
  2692.             list.onClosed.addListener(function() {
  2693.                 list = null;
  2694.                 maybeClose()
  2695.             });
  2696.             if (cb) {
  2697.                 controller = new Controller(receiverWindow.contentWindow, true);
  2698.                 cb(receiverWindow)
  2699.             }
  2700.         })
  2701.     }
  2702.  
  2703.     function connectSharedDevice(url) {
  2704.         showNotification("Vysor is connecting to a remote Android device");
  2705.         if (list) list.show();
  2706.         console.log("attempting to connect to shared device", url);
  2707.         if (!gcmSocketManager) {
  2708.             console.log("gcm not ready.");
  2709.             return
  2710.         }
  2711.         adbServer.start();
  2712.         var registrationId;
  2713.         var channel;
  2714.         var u = new URL(url);
  2715.         if (!(registrationId = getQueryVariable("registrationId", u)) || !(channel = getQueryVariable("channel", u))) {
  2716.             var splits = u.pathname.split("/");
  2717.             registrationId = splits[2];
  2718.             channel = splits[3]
  2719.         }
  2720.         var server = new Server;
  2721.         server.listen({
  2722.             port: 0,
  2723.             address: "127.0.0.1"
  2724.         }, function(socket) {
  2725.             var daemon = new AdbDaemon(new AdbTcpTransport(socket));
  2726.             gcmSocketManager.connect(registrationId, channel, function(gcmConn) {
  2727.                 tracker.sendEvent("connected-shared-device");
  2728.                 var serialno = "127.0.0.1:" + server.localPort;
  2729.                 vysorVirtualDevices[serialno] = gcmConn;
  2730.                 console.log("connected gcm socket");
  2731.                 gcmConn.openSocket = function() {
  2732.                     console.log("got a new socket? this should not happen...");
  2733.                     gcmConn.destroy()
  2734.                 };
  2735.                 daemon.openSocket = function(command, adbSocket) {
  2736.                     gcmConn.newSocket(command, function(repeaterSocket) {
  2737.                         Socket.stream(adbSocket, repeaterSocket, function() {})
  2738.                     })
  2739.                 };
  2740.                 gcmConn.onClose = function() {
  2741.                     daemon.destroy()
  2742.                 };
  2743.                 gcmConn.newSocket("properties", function(gcmSocket) {
  2744.                     readString(gcmSocket, function(properties) {
  2745.                         daemon.start(properties);
  2746.                         console.log("got properties", properties)
  2747.                     })
  2748.                 })
  2749.             })
  2750.         }.bind(this), function(result) {
  2751.             if (result) {
  2752.                 console.log("adb daemon failed to listen: " + result);
  2753.                 return
  2754.             }
  2755.             Adb.sendHostCommand("host:connect:127.0.0.1:" + server.localPort, function(socket, data) {
  2756.                 if (!socket) {
  2757.                     return
  2758.                 }
  2759.                 socket.destroy();
  2760.                 data = ab2str(data);
  2761.                 console.log(data)
  2762.             })
  2763.         }.bind(this))
  2764.     }
  2765.     chrome.app.runtime.onLaunched.addListener(function(e) {
  2766.         openList();
  2767.         if (e && e.id == "vysor_presentation") {
  2768.             connectSharedDevice(e.url);
  2769.             return
  2770.         }
  2771.         checkForUpdates()
  2772.     });
  2773.  
  2774.     function checkForUpdates() {
  2775.         chrome.runtime.requestUpdateCheck(function(status, details) {
  2776.             if (status == "update_available") {
  2777.                 notifyUpdateAvailable()
  2778.             }
  2779.         })
  2780.     }
  2781.     var gcmSocketManager;
  2782.     GcmRtcManager.start("64148182473", "AIzaSyDd7k1v017osyYbIC92fyf-36s3pv0z73U", {
  2783.         iceServers: [{
  2784.             url: "turn:n0.clockworkmod.com",
  2785.             username: "foo",
  2786.             credential: "bar"
  2787.         }, {
  2788.             url: "turn:n1.clockworkmod.com",
  2789.             username: "foo",
  2790.             credential: "bar"
  2791.         }]
  2792.     }, function(gcm) {
  2793.         gcmSocketManager = gcm;
  2794.         gcmSocketManager.sharedDevices = {}
  2795.     });
  2796.  
  2797.     function shareDevice(serialno, device) {
  2798.         var sharekey = Math.round(Math.random() * (1 << 30)).toString(16);
  2799.         gcmSocketManager.sharedDevices[serialno] = sharekey;
  2800.         var url = "https://vysor.clockworkmod.com/redirect/" + encodeURIComponent(gcmSocketManager.registrationId) + "/" + sharekey;
  2801.         url = "https://vysor.clockworkmod.com/app/vysor?registrationId=" + encodeURIComponent(gcmSocketManager.registrationId) + "&channel=" + sharekey;
  2802.         console.log(url);
  2803.         var properties = device.properties.substring(device.properties.indexOf("product")).replace(/ /g, ";").replace("device", "ro.product.device").replace("model", "ro.product.model").replace("product", "ro.product.name").replace(/:/g, "=");
  2804.         properties = "device::" + properties + ";";
  2805.         gcmSocketManager.listen(sharekey, function(gcmConn) {
  2806.             tracker.sendEvent("shared-device");
  2807.             if (!gcmSocketManager.sharedDevices[serialno]) {
  2808.                 gcmConn.destroy();
  2809.                 console.log("device is no longer being shared.");
  2810.                 return
  2811.             }
  2812.             console.log("accepted gcm socket");
  2813.             Adb.sendClientCommand({
  2814.                 serialno: serialno,
  2815.                 command: "shell:"
  2816.             }, function(shellSocket) {
  2817.                 shellSocket.onClose = function() {
  2818.                     gcmConn.destroy()
  2819.                 };
  2820.  
  2821.                 function reader() {
  2822.                     shellSocket.read(reader)
  2823.                 }
  2824.                 reader()
  2825.             });
  2826.             gcmConn.openSocket = function(command, adbSocket) {
  2827.                 if (command == "properties") {
  2828.                     adbSocket.write(str2ab(properties), function() {
  2829.                         console.log("sent properties", properties);
  2830.                         adbSocket.destroy()
  2831.                     });
  2832.                     return
  2833.                 }
  2834.                 if (command == "webstart") {
  2835.                     startMirrorServer(serialno, null, function(port, password) {
  2836.                         writeLine(adbSocket, password, function() {
  2837.                             console.log("sent password", password);
  2838.                             adbSocket.destroy()
  2839.                         });
  2840.                         Adb.shell({
  2841.                             command: "am start com.koushikdutta.vysor/.TipsActivity",
  2842.                             serialno: serialno
  2843.                         }, function() {})
  2844.                     });
  2845.                     return
  2846.                 }
  2847.                 Adb.sendClientCommand({
  2848.                     serialno: serialno,
  2849.                     command: command
  2850.                 }, function(socket) {
  2851.                     if (!socket) {
  2852.                         console.log("unable to execute adb proxy command?", command);
  2853.                         adbSocket.destroy();
  2854.                         return
  2855.                     }
  2856.                     Socket.stream(socket, adbSocket, function() {})
  2857.                 })
  2858.             }
  2859.         });
  2860.         copyTextToClipboard(url);
  2861.         var appName = chrome.runtime.getManifest().name;
  2862.         showNotification("Copied " + appName + " share URL to clipboard.");
  2863.         chrome.browser.openTab({
  2864.             url: "http://www.vysor.io/share#" + url
  2865.         })
  2866.     }
  2867.     var adbDevices = {};
  2868.     var hadAdbServer;
  2869.  
  2870.     function refreshList() {
  2871.         if (!list) {
  2872.             return
  2873.         }
  2874.         $(list.contentWindow.document).find("#devices").empty();
  2875.         var keys = Object.keys(adbDevices);
  2876.         if (!keys.length) {
  2877.             var ele = $('<li><a href="#">No devices found</li>');
  2878.             if (!hadAdbServer || adbServer.isRunning()) {
  2879.                 $(list.contentWindow.document).find("#choose-header").hide();
  2880.                 ele.hide()
  2881.             }
  2882.             $(list.contentWindow.document).find("#devices").append(ele)
  2883.         } else {
  2884.             $(list.contentWindow.document).find("#choose-header").show();
  2885.             $(keys).each(function(index, serialno) {
  2886.                 var d = adbDevices[serialno];
  2887.                 var device = d.name;
  2888.                 var display = device + " - " + serialno;
  2889.                 if (d.status == "unauthorized") display = serialno + " - unauthorized";
  2890.                 var ele = $('<li class="list-group-item"><span class="share badge">Share</span>' + display + "</li>");
  2891.                 $(list.contentWindow.document).find("#devices").append(ele);
  2892.                 var share = ele.find(".share");
  2893.                 if (!gcmSocketManager || vysorVirtualDevices[serialno]) share.hide();
  2894.                 ele.click(function() {
  2895.                     if (d.status == "unauthorized") {
  2896.                         showNotification('Check your Android device and click "Allow USB Debugging".')
  2897.                     } else {
  2898.                         tracker.sendEvent("click-device", device);
  2899.                         openWindow(serialno)
  2900.                     }
  2901.                 });
  2902.                 share.click(libind("Vysor Share", function(e) {
  2903.                     e.stopPropagation();
  2904.                     shareDevice(serialno, d)
  2905.                 }, function(e) {
  2906.                     e.stopPropagation()
  2907.                 }))
  2908.             })
  2909.         }
  2910.     }
  2911.     var hasRefreshedOnce;
  2912.  
  2913.     function initRefresher() {
  2914.         chrome.storage.local.get("connect-automatically", function(vals) {
  2915.             var connectAuto = vals["connect-automatically"] !== false;
  2916.             Adb.devices(function(devices) {
  2917.                 if (devices) {
  2918.                     hadAdbServer = true;
  2919.                     var newDevices = [];
  2920.                     var hasDevices;
  2921.                     $.each(devices, function(serialno, adb) {
  2922.                         hasDevices = true;
  2923.                         if (!adbDevices[serialno]) {
  2924.                             tracker.sendEvent("found-device", adb.name);
  2925.                             if (gcmSocketManager) {
  2926.                                 delete gcmSocketManager.sharedDevices[serialno]
  2927.                             }
  2928.                             var device = devices[serialno];
  2929.                             var isEmulator = device.properties.indexOf("emulator") != -1 || device.properties.indexOf("vbox") != -1;
  2930.                             if (!isEmulator && hasRefreshedOnce) {
  2931.                                 if (connectAuto || chrome.app.window.get(serialno) || adbServer.isRunning()) {
  2932.                                     openWindow(serialno, true);
  2933.                                     if (!list && !chrome.app.window.get(serialno)) {
  2934.                                         var appName = chrome.runtime.getManifest().name;
  2935.                                         chrome.notifications.create("never-start-automatically", {
  2936.                                             type: "basic",
  2937.                                             iconUrl: "/icon.png",
  2938.                                             title: appName,
  2939.                                             message: "Vysor has connected to an Android device and is starting.",
  2940.                                             buttons: [{
  2941.                                                 title: "Never Start Automatically"
  2942.                                             }]
  2943.                                         })
  2944.                                     }
  2945.                                 }
  2946.                             }
  2947.                         }
  2948.                     });
  2949.                     adbDevices = devices;
  2950.                     if (list) {
  2951.                         if (hasDevices) $(list.contentWindow.document).find("#not-found").hide();
  2952.                         else $(list.contentWindow.document).find("#not-found").show()
  2953.                     }
  2954.                 } else {
  2955.                     hadAdbServer = false;
  2956.                     adbDevices = {}
  2957.                 }
  2958.                 hasRefreshedOnce = true
  2959.             })
  2960.         })
  2961.     }
  2962.     initRefresher();
  2963.     var adbPort;
  2964.  
  2965.     function startAdbPort() {
  2966.         if (adbPort) return;
  2967.         adbPort = chrome.runtime.connectNative("com.clockworkmod.adb");
  2968.         adbPort.onDisconnect.addListener(function() {
  2969.             adbPort = null
  2970.         });
  2971.         adbPort.postMessage({
  2972.             command: "start-server"
  2973.         })
  2974.     }
  2975.  
  2976.     function refreshDevices() {
  2977.         if (list) {
  2978.             if (!hadAdbServer || adbServer.isRunning()) {
  2979.                 $(list.contentWindow.document).find("#connect-android").show();
  2980.                 $(list.contentWindow.document).find("#no-devices").hide()
  2981.             } else {
  2982.                 $(list.contentWindow.document).find("#connect-android").hide();
  2983.                 $(list.contentWindow.document).find("#no-devices").show()
  2984.             }
  2985.             if (!hadAdbServer) {
  2986.                 if (navigator.userAgent.indexOf("Windows NT 10") != -1 && adbPort == null) {
  2987.                     $(list.contentWindow.document).find("#adb-server-status").show();
  2988.                     $(list.contentWindow.document).find("#adb-server-status").html("Windows 10 users MUST download the latest <a href='http://koush.com/post/universal-adb-driver' target='_blank'> Universal ADB Drivers</a>")
  2989.                 } else {
  2990.                     $(list.contentWindow.document).find("#adb-server-status").hide()
  2991.                 }
  2992.                 startAdbPort()
  2993.             } else {
  2994.                 $(list.contentWindow.document).find("#adb-server-status").show();
  2995.                 if (adbServer.isRunning()) {
  2996.                     $(list.contentWindow.document).find("#adb-server-status").text("Vysor ADB Server started.")
  2997.                 } else {
  2998.                     $(list.contentWindow.document).find("#adb-server-status").text("ADB Server binary started.")
  2999.                 }
  3000.             }
  3001.         }
  3002.         initRefresher();
  3003.         refreshList();
  3004.         setTimeout(refreshDevices, 1e3)
  3005.     }
  3006.     refreshDevices();
  3007.     var updateNotifier;
  3008.  
  3009.     function notifyUpdateAvailable() {
  3010.         updateNotifier = throttleTimeout(updateNotifier, null, 1e4, function() {
  3011.             var appName = chrome.runtime.getManifest().name;
  3012.             chrome.notifications.create("reload", {
  3013.                 type: "basic",
  3014.                 iconUrl: "/icon.png",
  3015.                 title: appName,
  3016.                 message: "There is an update available for Vysor.",
  3017.                 buttons: [{
  3018.                     title: "Reload"
  3019.                 }]
  3020.             })
  3021.         })
  3022.     }
  3023.     chrome.runtime.onUpdateAvailable.addListener(function() {
  3024.         maybeClose()
  3025.     });
  3026.     chrome.notifications.onButtonClicked.addListener(function(nid, bid) {
  3027.         if (nid == "reload") {
  3028.             chrome.runtime.reload()
  3029.         } else if (nid == "never-start-automatically") {
  3030.             chrome.storage.local.set({
  3031.                 "connect-automatically": false
  3032.             });
  3033.             if (list) {
  3034.                 $(list.contentWindow.document).find("#connect-automatically-check").prop("checked", false)
  3035.             }
  3036.             chrome.notifications.clear(nid)
  3037.         }
  3038.     });
  3039.     chrome.notifications.onClicked.addListener(function(nid, bid) {});
  3040.     console.log("Vysor version", chrome.runtime.getManifest().version);
  3041.     console.log(navigator.userAgent)
  3042. })();
Add Comment
Please, Sign In to add comment