Configurate WebRTC to work across NATs

Using a public STUN server, allow the WebRTC connection to traverse
NATs.

This appears to work properly even when you do not have an Internet
connection. I also set up the options to make so that if there isn't
*any* non-loopback interface, things still work correctly.

Change-Id: Icb28f890bafa684c88be09afc45edc2039761362
diff --git a/aos/network/web_proxy.cc b/aos/network/web_proxy.cc
index 5902d30..317034c 100644
--- a/aos/network/web_proxy.cc
+++ b/aos/network/web_proxy.cc
@@ -308,6 +308,11 @@
       webrtc::PeerConnectionInterface::RTCConfiguration config;
       config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
       config.enable_dtls_srtp = true;
+      {
+        webrtc::PeerConnectionInterface::IceServer ice_server;
+        ice_server.urls.push_back("stun:stun.l.google.com:19302");
+        config.servers.push_back(ice_server);
+      }
 
       std::unique_ptr<rtc::Thread> signaling_thread = rtc::Thread::Create();
       signaling_thread->SetName("signaling_thread", nullptr);
@@ -319,6 +324,14 @@
       factory_deps.signaling_thread = signaling_thread.release();
       rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> factory =
           CreateModularPeerConnectionFactory(std::move(factory_deps));
+      {
+        // Don't ignore *any* networks--by default, the loopback interface is
+        // ignored, which makes it impossible to use WebRTC on devices with no
+        // network.
+        webrtc::PeerConnectionFactoryInterface::Options options;
+        options.network_ignore_mask = 0;
+        factory->SetOptions(options);
+      }
 
       peer_connection_ =
           factory->CreatePeerConnection(config, nullptr, nullptr, this);
diff --git a/aos/network/web_proxy.h b/aos/network/web_proxy.h
index e6d47c4..e7bf4e0 100644
--- a/aos/network/web_proxy.h
+++ b/aos/network/web_proxy.h
@@ -178,10 +178,16 @@
       rtc::scoped_refptr<webrtc::DataChannelInterface> channel) override;
   void OnRenegotiationNeeded() override {}
   void OnIceConnectionChange(
-      webrtc::PeerConnectionInterface::IceConnectionState /*state*/) override {}
+      webrtc::PeerConnectionInterface::IceConnectionState) override {}
   void OnIceGatheringChange(
       webrtc::PeerConnectionInterface::IceGatheringState) override {}
   void OnIceCandidate(const webrtc::IceCandidateInterface *candidate) override;
+  void OnIceCandidateError(const std::string &host_candidate,
+                           const std::string &url, int error_code,
+                           const std::string &error_text) override {
+    LOG(ERROR) << "ICE Candidate Error on " << host_candidate << " for " << url
+               << " with error " << error_code << ": " << error_text;
+  }
   void OnIceConnectionReceivingChange(bool) override {}
 
   // CreateSessionDescriptionObserver implementation
diff --git a/aos/network/www/proxy.ts b/aos/network/www/proxy.ts
index 02573b3..3da817c 100644
--- a/aos/network/www/proxy.ts
+++ b/aos/network/www/proxy.ts
@@ -232,6 +232,10 @@
     this.webSocketConnection.send(array.buffer.slice(array.byteOffset));
   }
 
+  onIceCandidateError(e: RTCPeerConnectionIceErrorEvent): void {
+    console.warn(e);
+  }
+
   // Called for new SDPs. Make sure to set it locally and remotely.
   onOfferCreated(description: RTCSessionDescriptionInit): void {
     this.rtcPeerConnection.setLocalDescription(description);
@@ -251,16 +255,17 @@
   // We now have a websocket, so start setting up the peer connection. We only
   // want a DataChannel, so create it and then create an offer to send.
   onWebSocketOpen(): void {
-    this.rtcPeerConnection = new RTCPeerConnection({});
+    this.rtcPeerConnection = new RTCPeerConnection(
+        {'iceServers': [{'urls': ['stun:stun.l.google.com:19302']}]});
     this.rtcPeerConnection.addEventListener(
         'datachannel', (e) => this.onDataChannel(e));
     this.dataChannel = this.rtcPeerConnection.createDataChannel('signalling');
     this.handlers.add(
         new Handler((data) => this.onConfigMessage(data), this.dataChannel));
-    // TODO(james): Is this used? Can we delete it?
-    // window.dc = this.dataChannel;
     this.rtcPeerConnection.addEventListener(
         'icecandidate', (e) => this.onIceCandidate(e));
+    this.rtcPeerConnection.addEventListener(
+        'icecandidateerror', (e) => this.onIceCandidateError(e));
     this.rtcPeerConnection.createOffer().then(
         (offer) => this.onOfferCreated(offer));
   }