Bug 852665: add support for ICE disconnected state. r=bwc draft
authorNils Ohlmeier [:drno] <drno@ohlmeier.org>
Fri, 28 Oct 2016 22:22:17 -0700
changeset 432361 4f99ebbd4fa0e566965f94dc7b5d999bbbfd7b85
parent 431012 1561c917ee27c3ea04bd69467e5b8c7c08102f2a
child 535623 4a5d3f7dcdb610f2c4a8eb45791de0e0e79cd145
push id34281
push userdrno@ohlmeier.org
push dateTue, 01 Nov 2016 19:56:17 +0000
reviewersbwc
bugs852665
milestone52.0a1
Bug 852665: add support for ICE disconnected state. r=bwc MozReview-Commit-ID: 5ONTc2Wz72O
media/mtransport/nricectx.cpp
media/mtransport/nricectx.h
media/mtransport/test/ice_unittest.cpp
media/mtransport/third_party/nICEr/src/ice/ice_component.c
media/mtransport/third_party/nICEr/src/ice/ice_handler.h
media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c
media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c
media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.h
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
--- a/media/mtransport/nricectx.cpp
+++ b/media/mtransport/nricectx.cpp
@@ -331,30 +331,41 @@ int NrIceCtx::ice_checking(void *obj, nr
   // Get the ICE ctx
   NrIceCtx *ctx = static_cast<NrIceCtx *>(obj);
 
   ctx->SetConnectionState(ICE_CTX_CHECKING);
 
   return 0;
 }
 
-int NrIceCtx::ice_completed(void *obj, nr_ice_peer_ctx *pctx) {
-  MOZ_MTLOG(ML_DEBUG, "ice_completed called");
+int NrIceCtx::ice_connected(void *obj, nr_ice_peer_ctx *pctx) {
+  MOZ_MTLOG(ML_DEBUG, "ice_connected called");
 
   // Get the ICE ctx
   NrIceCtx *ctx = static_cast<NrIceCtx *>(obj);
 
   // This is called even on failed contexts.
   if (ctx->connection_state() != ICE_CTX_FAILED) {
-    ctx->SetConnectionState(ICE_CTX_OPEN);
+    ctx->SetConnectionState(ICE_CTX_CONNECTED);
   }
 
   return 0;
 }
 
+int NrIceCtx::ice_disconnected(void *obj, nr_ice_peer_ctx *pctx) {
+  MOZ_MTLOG(ML_DEBUG, "ice_disconnected called");
+
+  // Get the ICE ctx
+  NrIceCtx *ctx = static_cast<NrIceCtx *>(obj);
+
+  ctx->SetConnectionState(ICE_CTX_DISCONNECTED);
+
+  return 0;
+}
+
 int NrIceCtx::msg_recvd(void *obj, nr_ice_peer_ctx *pctx,
                         nr_ice_media_stream *stream, int component_id,
                         UCHAR *msg, int len) {
   // Get the ICE ctx
   NrIceCtx *ctx = static_cast<NrIceCtx *>(obj);
   RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
 
   // Streams which do not exist should never have packets.
@@ -608,19 +619,20 @@ NrIceCtx::Initialize(const std::string& 
     SetNat(test_nat);
   }
 
   // Create the handler objects
   ice_handler_vtbl_ = new nr_ice_handler_vtbl();
   ice_handler_vtbl_->select_pair = &NrIceCtx::select_pair;
   ice_handler_vtbl_->stream_ready = &NrIceCtx::stream_ready;
   ice_handler_vtbl_->stream_failed = &NrIceCtx::stream_failed;
-  ice_handler_vtbl_->ice_completed = &NrIceCtx::ice_completed;
+  ice_handler_vtbl_->ice_connected = &NrIceCtx::ice_connected;
   ice_handler_vtbl_->msg_recvd = &NrIceCtx::msg_recvd;
   ice_handler_vtbl_->ice_checking = &NrIceCtx::ice_checking;
+  ice_handler_vtbl_->ice_disconnected = &NrIceCtx::ice_disconnected;
 
   ice_handler_ = new nr_ice_handler();
   ice_handler_->vtbl = ice_handler_vtbl_;
   ice_handler_->obj = this;
 
   // Create the peer ctx. Because we do not support parallel forking, we
   // only have one peer ctx.
   std::string peer_name = name_ + ":default";
--- a/media/mtransport/nricectx.h
+++ b/media/mtransport/nricectx.h
@@ -191,18 +191,21 @@ class NrIceProxyServer {
 
 class TestNat;
 
 class NrIceCtx {
  friend class NrIceCtxHandler;
  public:
   enum ConnectionState { ICE_CTX_INIT,
                          ICE_CTX_CHECKING,
-                         ICE_CTX_OPEN,
-                         ICE_CTX_FAILED
+                         ICE_CTX_CONNECTED,
+                         ICE_CTX_COMPLETED,
+                         ICE_CTX_FAILED,
+                         ICE_CTX_DISCONNECTED,
+                         ICE_CTX_CLOSED
   };
 
   enum GatheringState { ICE_CTX_GATHER_INIT,
                         ICE_CTX_GATHER_STARTED,
                         ICE_CTX_GATHER_COMPLETE
   };
 
   enum Controlling { ICE_CONTROLLING,
@@ -344,17 +347,18 @@ private:
 
   // Handler implementation
   static int select_pair(void *obj,nr_ice_media_stream *stream,
                          int component_id, nr_ice_cand_pair **potentials,
                          int potential_ct);
   static int stream_ready(void *obj, nr_ice_media_stream *stream);
   static int stream_failed(void *obj, nr_ice_media_stream *stream);
   static int ice_checking(void *obj, nr_ice_peer_ctx *pctx);
-  static int ice_completed(void *obj, nr_ice_peer_ctx *pctx);
+  static int ice_connected(void *obj, nr_ice_peer_ctx *pctx);
+  static int ice_disconnected(void *obj, nr_ice_peer_ctx *pctx);
   static int msg_recvd(void *obj, nr_ice_peer_ctx *pctx,
                        nr_ice_media_stream *stream, int component_id,
                        unsigned char *msg, int len);
   static void trickle_cb(void *arg, nr_ice_ctx *ctx, nr_ice_media_stream *stream,
                          int component_id, nr_ice_candidate *candidate);
 
   // Find a media stream by stream ptr. Gross
   RefPtr<NrIceMediaStream> FindStream(nr_ice_media_stream *stream);
--- a/media/mtransport/test/ice_unittest.cpp
+++ b/media/mtransport/test/ice_unittest.cpp
@@ -388,17 +388,18 @@ class IceTestPeer : public sigslot::has_
       name_(name),
       ice_ctx_(NrIceCtxHandler::Create(name, offerer, allow_loopback,
                                        enable_tcp, allow_link_local,
                                        ice_policy)),
       candidates_(),
       shutting_down_(false),
       gathering_complete_(false),
       ready_ct_(0),
-      ice_complete_(false),
+      ice_connected_(false),
+      ice_failed_(false),
       ice_reached_checking_(false),
       received_(0),
       sent_(0),
       fake_resolver_(),
       dns_resolver_(new NrIceResolver()),
       remote_(nullptr),
       candidate_filter_(nullptr),
       expected_local_type_(NrIceCandidate::ICE_HOST),
@@ -679,17 +680,18 @@ class IceTestPeer : public sigslot::has_
   bool is_ready(size_t stream)
   {
     bool result;
     test_utils_->sts_target()->Dispatch(
         WrapRunnableRet(&result, this, &IceTestPeer::is_ready_s, stream),
         NS_DISPATCH_SYNC);
     return result;
   }
-  bool ice_complete() { return ice_complete_; }
+  bool ice_connected() { return ice_connected_; }
+  bool ice_failed() { return ice_failed_; }
   bool ice_reached_checking() { return ice_reached_checking_; }
   size_t received() { return received_; }
   size_t sent() { return sent_; }
 
 
   void RestartIce() {
     test_utils_->sts_target()->Dispatch(
         WrapRunnable(this,
@@ -708,17 +710,18 @@ class IceTestPeer : public sigslot::has_
         &IceTestPeer::GatheringStateChange);
     ice_ctx_->ctx()->SignalConnectionStateChange.connect(
         this,
         &IceTestPeer::ConnectionStateChange);
 
     // take care of some local bookkeeping
     ready_ct_ = 0;
     gathering_complete_ = false;
-    ice_complete_ = false;
+    ice_connected_ = false;
+    ice_failed_ = false;
     ice_reached_checking_ = false;
     remote_ = nullptr;
   }
 
 
   void FinalizeIceRestart() {
     test_utils_->sts_target()->Dispatch(
         WrapRunnable(this, &IceTestPeer::FinalizeIceRestart_s),
@@ -746,17 +749,18 @@ class IceTestPeer : public sigslot::has_
   // Start connecting to another peer
   void Connect_s(IceTestPeer *remote, TrickleMode trickle_mode,
                  bool start = true) {
     nsresult res;
 
     remote_ = remote;
 
     trickle_mode_ = trickle_mode;
-    ice_complete_ = false;
+    ice_connected_ = false;
+    ice_failed_ = false;
     ice_reached_checking_ = false;
     res = ice_ctx_->ctx()->ParseGlobalAttributes(remote->GetGlobalAttributes());
     ASSERT_TRUE(NS_SUCCEEDED(res));
 
     if (trickle_mode == TRICKLE_NONE ||
         trickle_mode == TRICKLE_REAL) {
       for (size_t i=0; i<ice_ctx_->ctx()->GetStreamCount(); ++i) {
         RefPtr<NrIceMediaStream> aStream = ice_ctx_->ctx()->GetStream(i);
@@ -1220,25 +1224,36 @@ class IceTestPeer : public sigslot::has_
 
   void ConnectionStateChange(NrIceCtx* ctx,
                              NrIceCtx::ConnectionState state) {
     (void)ctx;
     switch (state) {
       case NrIceCtx::ICE_CTX_INIT:
         break;
       case NrIceCtx::ICE_CTX_CHECKING:
-        std::cerr << name_ << " ICE reached checking " << std::endl;
+        std::cerr << name_ << " ICE reached checking" << std::endl;
         ice_reached_checking_ = true;
         break;
-      case NrIceCtx::ICE_CTX_OPEN:
-        std::cerr << name_ << " ICE completed " << std::endl;
-        ice_complete_ = true;
+      case NrIceCtx::ICE_CTX_CONNECTED:
+        std::cerr << name_ << " ICE connected" << std::endl;
+        ice_connected_ = true;
+        break;
+      case NrIceCtx::ICE_CTX_COMPLETED:
+        std::cerr << name_ << " ICE completed" << std::endl;
         break;
       case NrIceCtx::ICE_CTX_FAILED:
+        std::cerr << name_ << " ICE failed" << std::endl;
+        ice_failed_ = true;
         break;
+      case NrIceCtx::ICE_CTX_DISCONNECTED:
+        std::cerr << name_ << " ICE disconnected" << std::endl;
+        ice_connected_ = false;
+        break;
+      default:
+        MOZ_CRASH();
     }
   }
 
   void PacketReceived(NrIceMediaStream *stream, int component, const unsigned char *data,
                       int len) {
     std::cerr << name_ << ": received " << len << " bytes" << std::endl;
     ++received_;
   }
@@ -1405,17 +1420,18 @@ class IceTestPeer : public sigslot::has_
   RefPtr<NrIceCtxHandler> ice_ctx_;
   std::map<std::string, std::vector<std::string> > candidates_;
   // Maps from stream id to list of remote trickle candidates
   std::map<size_t, std::vector<SchedulableTrickleCandidate*> >
     controlled_trickle_candidates_;
   bool shutting_down_;
   bool gathering_complete_;
   int ready_ct_;
-  bool ice_complete_;
+  bool ice_connected_;
+  bool ice_failed_;
   bool ice_reached_checking_;
   size_t received_;
   size_t sent_;
   struct timeval consent_timestamp_;
   NrIceResolverFake fake_resolver_;
   RefPtr<NrIceResolver> dns_resolver_;
   IceTestPeer *remote_;
   CandidateFilter candidate_filter_;
@@ -1786,32 +1802,32 @@ class WebRtcIceConnectTest : public Stun
   }
 
   void Connect() {
     ConnectCallerAndCallee(p1_.get(), p2_.get());
   }
 
   void ConnectCallerAndCallee(IceTestPeer* caller, IceTestPeer* callee) {
     ASSERT_TRUE(caller->ready_ct() == 0);
-    ASSERT_TRUE(caller->ice_complete() == 0);
+    ASSERT_TRUE(caller->ice_connected() == 0);
     ASSERT_TRUE(caller->ice_reached_checking() == 0);
     ASSERT_TRUE(callee->ready_ct() == 0);
-    ASSERT_TRUE(callee->ice_complete() == 0);
+    ASSERT_TRUE(callee->ice_connected() == 0);
     ASSERT_TRUE(callee->ice_reached_checking() == 0);
 
     // IceTestPeer::Connect grabs attributes from the first arg, and
     // gives them to |this|, meaning that callee->Connect(caller, ...)
     // simulates caller sending an offer to callee. Order matters here
     // because it determines which peer is controlling.
     callee->Connect(caller, TRICKLE_NONE);
     caller->Connect(callee, TRICKLE_NONE);
 
     ASSERT_TRUE_WAIT(caller->ready_ct() == 1 && callee->ready_ct() == 1,
                      kDefaultTimeout);
-    ASSERT_TRUE_WAIT(caller->ice_complete() && callee->ice_complete(),
+    ASSERT_TRUE_WAIT(caller->ice_connected() && callee->ice_connected(),
                      kDefaultTimeout);
 
     ASSERT_TRUE(caller->ice_reached_checking());
     ASSERT_TRUE(callee->ice_reached_checking());
 
     caller->DumpAndCheckActiveCandidates();
     callee->DumpAndCheckActiveCandidates();
   }
@@ -1836,33 +1852,52 @@ class WebRtcIceConnectTest : public Stun
   void ConnectP1(TrickleMode mode = TRICKLE_NONE) {
     p1_->Connect(p2_.get(), mode);
   }
 
   void ConnectP2(TrickleMode mode = TRICKLE_NONE) {
     p2_->Connect(p1_.get(), mode);
   }
 
-  void WaitForComplete(int expected_streams = 1) {
+  void WaitForConnectedStreams(int expected_streams = 1) {
     ASSERT_TRUE_WAIT(p1_->ready_ct() == expected_streams &&
                      p2_->ready_ct() == expected_streams, kDefaultTimeout);
-    ASSERT_TRUE_WAIT(p1_->ice_complete() && p2_->ice_complete(),
+    ASSERT_TRUE_WAIT(p1_->ice_connected() && p2_->ice_connected(),
                      kDefaultTimeout);
   }
 
   void AssertCheckingReached() {
     ASSERT_TRUE(p1_->ice_reached_checking());
     ASSERT_TRUE(p2_->ice_reached_checking());
   }
 
+  void WaitForConnected(unsigned int timeout = kDefaultTimeout) {
+    ASSERT_TRUE_WAIT(p1_->ice_connected(), timeout);
+    ASSERT_TRUE_WAIT(p2_->ice_connected(), timeout);
+  }
+
   void WaitForGather() {
     ASSERT_TRUE_WAIT(p1_->gathering_complete(), kDefaultTimeout);
     ASSERT_TRUE_WAIT(p2_->gathering_complete(), kDefaultTimeout);
   }
 
+  void WaitForDisconnected(unsigned int timeout = kDefaultTimeout) {
+    ASSERT_TRUE(p1_->ice_connected());
+    ASSERT_TRUE(p2_->ice_connected());
+    ASSERT_TRUE_WAIT(p1_->ice_connected() == 0 &&
+                     p2_->ice_connected() == 0,
+                     timeout);
+  }
+
+  void WaitForFailed(unsigned int timeout = kDefaultTimeout) {
+    ASSERT_TRUE_WAIT(p1_->ice_failed() &&
+                     p2_->ice_failed(),
+                     timeout);
+  }
+
   void ConnectTrickle(TrickleMode trickle = TRICKLE_SIMULATE) {
     p2_->Connect(p1_.get(), trickle);
     p1_->Connect(p2_.get(), trickle);
   }
 
   void SimulateTrickle(size_t stream) {
     p1_->SimulateTrickle(stream);
     p2_->SimulateTrickle(stream);
@@ -1873,19 +1908,16 @@ class WebRtcIceConnectTest : public Stun
   void SimulateTrickleP1(size_t stream) {
     p1_->SimulateTrickle(stream);
   }
 
   void SimulateTrickleP2(size_t stream) {
     p2_->SimulateTrickle(stream);
   }
 
-  void VerifyConnected() {
-  }
-
   void CloseP1() {
     p1_->Close();
   }
 
   void ConnectThenDelete() {
     p2_->Connect(p1_.get(), TRICKLE_NONE, false);
     p1_->Connect(p2_.get(), TRICKLE_NONE, true);
     test_utils_->sts_target()->Dispatch(WrapRunnable(this,
@@ -2734,43 +2766,40 @@ TEST_F(WebRtcIceConnectTest, TestTrickle
   AddStream(1);
   p1_->SetTiebreaker(1);
   p2_->SetTiebreaker(0);
   ASSERT_TRUE(Gather());
   p1_->SetControlling(NrIceCtx::ICE_CONTROLLING);
   p2_->SetControlling(NrIceCtx::ICE_CONTROLLING);
   ConnectTrickle();
   SimulateTrickle(0);
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
+  WaitForConnected(1000);
   AssertCheckingReached();
 }
 
 TEST_F(WebRtcIceConnectTest, TestTrickleBothControllingP2Wins) {
   AddStream(1);
   p1_->SetTiebreaker(0);
   p2_->SetTiebreaker(1);
   ASSERT_TRUE(Gather());
   p1_->SetControlling(NrIceCtx::ICE_CONTROLLING);
   p2_->SetControlling(NrIceCtx::ICE_CONTROLLING);
   ConnectTrickle();
   SimulateTrickle(0);
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
+  WaitForConnected(1000);
   AssertCheckingReached();
 }
 
 TEST_F(WebRtcIceConnectTest, TestTrickleIceLiteOfferer) {
   AddStream(1);
   ASSERT_TRUE(Gather());
   p1_->SimulateIceLite();
   ConnectTrickle();
   SimulateTrickle(0);
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
+  WaitForConnected(1000);
   AssertCheckingReached();
 }
 
 TEST_F(WebRtcIceConnectTest, TestGatherFullCone) {
   UseNat();
   AddStream(1);
   ASSERT_TRUE(Gather());
 }
@@ -2962,70 +2991,68 @@ TEST_F(WebRtcIceConnectTest, TestConnect
 
 
 TEST_F(WebRtcIceConnectTest, TestConnectP2ThenP1) {
   AddStream(1);
   ASSERT_TRUE(Gather());
   ConnectP2();
   PR_Sleep(1000);
   ConnectP1();
-  WaitForComplete();
+  WaitForConnectedStreams();
 }
 
 TEST_F(WebRtcIceConnectTest, TestConnectP2ThenP1Trickle) {
   AddStream(1);
   ASSERT_TRUE(Gather());
   ConnectP2();
   PR_Sleep(1000);
   ConnectP1(TRICKLE_SIMULATE);
   SimulateTrickleP1(0);
-  WaitForComplete();
+  WaitForConnectedStreams();
 }
 
 TEST_F(WebRtcIceConnectTest, TestConnectP2ThenP1TrickleTwoComponents) {
   AddStream(1);
   AddStream(2);
   ASSERT_TRUE(Gather());
   ConnectP2();
   PR_Sleep(1000);
   ConnectP1(TRICKLE_SIMULATE);
   SimulateTrickleP1(0);
   std::cerr << "Sleeping between trickle streams" << std::endl;
   PR_Sleep(1000);  // Give this some time to settle but not complete
                    // all of ICE.
   SimulateTrickleP1(1);
-  WaitForComplete(2);
+  WaitForConnectedStreams(2);
 }
 
 TEST_F(WebRtcIceConnectTest, TestConnectAutoPrioritize) {
   Init(false, false);
   AddStream(1);
   ASSERT_TRUE(Gather());
   Connect();
 }
 
 TEST_F(WebRtcIceConnectTest, TestConnectTrickleOneStreamOneComponent) {
   AddStream(1);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
   SimulateTrickle(0);
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
+  WaitForConnected(1000);
   AssertCheckingReached();
 }
 
 TEST_F(WebRtcIceConnectTest, TestConnectTrickleTwoStreamsOneComponent) {
   AddStream(1);
   AddStream(1);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
   SimulateTrickle(0);
   SimulateTrickle(1);
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
+  WaitForConnected(1000);
   AssertCheckingReached();
 }
 
 void RealisticTrickleDelay(
     std::vector<SchedulableTrickleCandidate*>& candidates) {
   for (size_t i = 0; i < candidates.size(); ++i) {
     SchedulableTrickleCandidate* cand = candidates[i];
     if (cand->IsHost()) {
@@ -3097,144 +3124,132 @@ TEST_F(WebRtcIceConnectTest, TestConnect
   AddStream(1);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
   RealisticTrickleDelay(p1_->ControlTrickle(0));
   RealisticTrickleDelay(p2_->ControlTrickle(0));
   AddStream(1);
   RealisticTrickleDelay(p1_->ControlTrickle(1));
   RealisticTrickleDelay(p2_->ControlTrickle(1));
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
+  WaitForConnected(1000);
   AssertCheckingReached();
 }
 
 TEST_F(WebRtcIceConnectTest, TestConnectTrickleAddStreamAfterICE) {
   AddStream(1);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
   RealisticTrickleDelay(p1_->ControlTrickle(0));
   RealisticTrickleDelay(p2_->ControlTrickle(0));
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
+  WaitForConnected(1000);
   AddStream(1);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
   RealisticTrickleDelay(p1_->ControlTrickle(1));
   RealisticTrickleDelay(p2_->ControlTrickle(1));
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
+  WaitForConnected(1000);
   AssertCheckingReached();
 }
 
 TEST_F(WebRtcIceConnectTest, RemoveStream) {
   AddStream(1);
   AddStream(1);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
   RealisticTrickleDelay(p1_->ControlTrickle(0));
   RealisticTrickleDelay(p2_->ControlTrickle(0));
   RealisticTrickleDelay(p1_->ControlTrickle(1));
   RealisticTrickleDelay(p2_->ControlTrickle(1));
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
+  WaitForConnected(1000);
 
   RemoveStream(0);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
 }
 
 TEST_F(WebRtcIceConnectTest, P1NoTrickle) {
   AddStream(1);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
   DropTrickleCandidates(p1_->ControlTrickle(0));
   RealisticTrickleDelay(p2_->ControlTrickle(0));
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
+  WaitForConnected(1000);
 }
 
 TEST_F(WebRtcIceConnectTest, P2NoTrickle) {
   AddStream(1);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
   RealisticTrickleDelay(p1_->ControlTrickle(0));
   DropTrickleCandidates(p2_->ControlTrickle(0));
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
+  WaitForConnected(1000);
 }
 
 TEST_F(WebRtcIceConnectTest, RemoveAndAddStream) {
   AddStream(1);
   AddStream(1);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
   RealisticTrickleDelay(p1_->ControlTrickle(0));
   RealisticTrickleDelay(p2_->ControlTrickle(0));
   RealisticTrickleDelay(p1_->ControlTrickle(1));
   RealisticTrickleDelay(p2_->ControlTrickle(1));
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
+  WaitForConnected(1000);
 
   RemoveStream(0);
   AddStream(1);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
   RealisticTrickleDelay(p1_->ControlTrickle(2));
   RealisticTrickleDelay(p2_->ControlTrickle(2));
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
+  WaitForConnected(1000);
 }
 
 TEST_F(WebRtcIceConnectTest, RemoveStreamBeforeGather) {
   AddStream(1);
   AddStream(1);
   ASSERT_TRUE(Gather(0));
   RemoveStream(0);
   WaitForGather();
   ConnectTrickle();
   RealisticTrickleDelay(p1_->ControlTrickle(1));
   RealisticTrickleDelay(p2_->ControlTrickle(1));
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
+  WaitForConnected(1000);
 }
 
 TEST_F(WebRtcIceConnectTest, RemoveStreamDuringGather) {
   AddStream(1);
   AddStream(1);
   RemoveStream(0);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
   RealisticTrickleDelay(p1_->ControlTrickle(1));
   RealisticTrickleDelay(p2_->ControlTrickle(1));
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
+  WaitForConnected(1000);
 }
 
 TEST_F(WebRtcIceConnectTest, RemoveStreamDuringConnect) {
   AddStream(1);
   AddStream(1);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
   RealisticTrickleDelay(p1_->ControlTrickle(0));
   RealisticTrickleDelay(p2_->ControlTrickle(0));
   RealisticTrickleDelay(p1_->ControlTrickle(1));
   RealisticTrickleDelay(p2_->ControlTrickle(1));
   RemoveStream(0);
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
+  WaitForConnected(1000);
 }
 
 TEST_F(WebRtcIceConnectTest, TestConnectRealTrickleOneStreamOneComponent) {
   AddStream(1);
   AddStream(1);
   ASSERT_TRUE(Gather(0));
   ConnectTrickle(TRICKLE_REAL);
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), kDefaultTimeout);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), kDefaultTimeout);
+  WaitForConnected();
   WaitForGather();  // ICE can complete before we finish gathering.
   AssertCheckingReached();
 }
 
 TEST_F(WebRtcIceConnectTest, TestSendReceive) {
   AddStream(1);
   ASSERT_TRUE(Gather());
   Connect();
@@ -3285,35 +3300,44 @@ TEST_F(WebRtcIceConnectTest, TestConsent
   SendReceive();
 }
 
 TEST_F(WebRtcIceConnectTest, TestConsentIntermittent) {
   AddStream(1);
   SetupAndCheckConsent();
   p1_->SetBlockStun(true);
   p2_->SetBlockStun(true);
-  PR_Sleep(1000);
+  WaitForDisconnected();
   AssertConsentRefresh(CONSENT_STALE);
   SendReceive();
   p1_->SetBlockStun(false);
   p2_->SetBlockStun(false);
-  PR_Sleep(600);
+  WaitForConnected();
   AssertConsentRefresh();
   SendReceive();
+  p1_->SetBlockStun(true);
+  p2_->SetBlockStun(true);
+  WaitForDisconnected();
+  AssertConsentRefresh(CONSENT_STALE);
+  SendReceive();
+  p1_->SetBlockStun(false);
+  p2_->SetBlockStun(false);
+  WaitForConnected();
+  AssertConsentRefresh();
 }
 
 TEST_F(WebRtcIceConnectTest, TestConsentTimeout) {
   AddStream(1);
   SetupAndCheckConsent();
   p1_->SetBlockStun(true);
   p2_->SetBlockStun(true);
-  PR_Sleep(600);
+  WaitForDisconnected();
   AssertConsentRefresh(CONSENT_STALE);
   SendReceive();
-  PR_Sleep(2500);
+  WaitForFailed();
   AssertConsentRefresh(CONSENT_EXPIRED);
   SendFailure();
 }
 
 TEST_F(WebRtcIceConnectTest, TestConsentDelayed) {
   AddStream(1);
   SetupAndCheckConsent();
   /* Note: We don't have a list of STUN transaction IDs of the previously timed
@@ -3345,50 +3369,48 @@ TEST_F(WebRtcIceConnectTest, TestConnect
   SetTurnServer(turn_server_, kDefaultStunServerPort,
                 turn_user_, turn_password_);
   SetCandidateFilter(SabotageHostCandidateAndDropReflexive);
   p1_->Gather();
   PR_Sleep(500);
   p2_->Gather();
   ConnectTrickle(TRICKLE_REAL);
   WaitForGather();
-  WaitForComplete();
+  WaitForConnectedStreams();
 }
 
 TEST_F(WebRtcIceConnectTest, TestConnectTurnWithNormalTrickleDelay) {
   if (turn_server_.empty())
     return;
 
   AddStream(1);
   SetTurnServer(turn_server_, kDefaultStunServerPort,
                 turn_user_, turn_password_);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
   RealisticTrickleDelay(p1_->ControlTrickle(0));
   RealisticTrickleDelay(p2_->ControlTrickle(0));
 
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), kDefaultTimeout);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), kDefaultTimeout);
+  WaitForConnected();
   AssertCheckingReached();
 }
 
 TEST_F(WebRtcIceConnectTest, TestConnectTurnWithNormalTrickleDelayOneSided) {
   if (turn_server_.empty())
     return;
 
   AddStream(1);
   SetTurnServer(turn_server_, kDefaultStunServerPort,
                 turn_user_, turn_password_);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
   RealisticTrickleDelay(p1_->ControlTrickle(0));
   p2_->SimulateTrickle(0);
 
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), kDefaultTimeout);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), kDefaultTimeout);
+  WaitForConnected();
   AssertCheckingReached();
 }
 
 TEST_F(WebRtcIceConnectTest, TestConnectTurnWithLargeTrickleDelay) {
   if (turn_server_.empty())
     return;
 
   AddStream(1);
@@ -3396,18 +3418,17 @@ TEST_F(WebRtcIceConnectTest, TestConnect
                 turn_user_, turn_password_);
   SetCandidateFilter(SabotageHostCandidateAndDropReflexive);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
   // Trickle host candidates immediately, but delay relay candidates
   DelayRelayCandidates(p1_->ControlTrickle(0), 3700);
   DelayRelayCandidates(p2_->ControlTrickle(0), 3700);
 
-  ASSERT_TRUE_WAIT(p1_->ice_complete(), kDefaultTimeout);
-  ASSERT_TRUE_WAIT(p2_->ice_complete(), kDefaultTimeout);
+  WaitForConnected();
   AssertCheckingReached();
 }
 
 TEST_F(WebRtcIceConnectTest, TestConnectTurnTcp) {
   if (turn_server_.empty())
     return;
 
   AddStream(1);
@@ -3634,17 +3655,17 @@ TEST_F(WebRtcIceConnectTest, TestPollCan
   p1_->StartChecks();
   p1_->UpdateAndValidateCandidatePairs(0, &pairs1);
   p2_->UpdateAndValidateCandidatePairs(0, &pairs2);
 
   p2_->StartChecks();
   p1_->UpdateAndValidateCandidatePairs(0, &pairs1);
   p2_->UpdateAndValidateCandidatePairs(0, &pairs2);
 
-  WaitForComplete();
+  WaitForConnectedStreams();
   p1_->UpdateAndValidateCandidatePairs(0, &pairs1);
   p2_->UpdateAndValidateCandidatePairs(0, &pairs2);
   ASSERT_TRUE(ContainsSucceededPair(pairs1));
   ASSERT_TRUE(ContainsSucceededPair(pairs2));
 }
 
 TEST_F(WebRtcIceConnectTest, TestRLogConnector) {
   AddStream(1);
@@ -3659,17 +3680,17 @@ TEST_F(WebRtcIceConnectTest, TestRLogCon
   p1_->StartChecks();
   p1_->UpdateAndValidateCandidatePairs(0, &pairs1);
   p2_->UpdateAndValidateCandidatePairs(0, &pairs2);
 
   p2_->StartChecks();
   p1_->UpdateAndValidateCandidatePairs(0, &pairs1);
   p2_->UpdateAndValidateCandidatePairs(0, &pairs2);
 
-  WaitForComplete();
+  WaitForConnectedStreams();
   p1_->UpdateAndValidateCandidatePairs(0, &pairs1);
   p2_->UpdateAndValidateCandidatePairs(0, &pairs2);
   ASSERT_TRUE(ContainsSucceededPair(pairs1));
   ASSERT_TRUE(ContainsSucceededPair(pairs2));
 
   for (auto p = pairs1.begin(); p != pairs1.end(); ++p) {
     std::deque<std::string> logs;
     std::string substring("CAND-PAIR(");
--- a/media/mtransport/third_party/nICEr/src/ice/ice_component.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c
@@ -1194,33 +1194,46 @@ static void nr_ice_component_consent_fai
   }
 
 static void nr_ice_component_consent_timeout_cb(NR_SOCKET s, int how, void *cb_arg)
   {
     nr_ice_component *comp=cb_arg;
 
     comp->consent_timeout = 0;
 
-    r_log(LOG_ICE,LOG_WARNING,"ICE(%s)/STREAM(%s)/COMP(%d): Consent refresh timed out",
+    r_log(LOG_ICE,LOG_WARNING,"ICE(%s)/STREAM(%s)/COMP(%d): Consent refresh final time out",
           comp->ctx->label, comp->stream->label, comp->component_id);
     nr_ice_component_consent_failed(comp);
   }
 
+
+static void nr_ice_component_consent_request_timed_out(nr_ice_component *comp)
+  {
+    if (!comp->can_send) {
+      return;
+    }
+
+    nr_ice_peer_ctx_disconnected(comp->stream->pctx);
+  }
+
 static void nr_ice_component_consent_refreshed(nr_ice_component *comp)
   {
     uint16_t tval;
 
     if (!comp->can_send) {
       return;
     }
 
     gettimeofday(&comp->consent_last_seen, 0);
     r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/COMP(%d): consent_last_seen is now %lu",
         comp->ctx->label, comp->stream->label, comp->component_id,
         comp->consent_last_seen.tv_sec);
+
+    nr_ice_peer_ctx_connected(comp->stream->pctx);
+
     if (comp->consent_timeout)
       NR_async_timer_cancel(comp->consent_timeout);
 
     tval = NR_ICE_CONSENT_TIMEOUT_DEFAULT;
     if (comp->ctx->test_timer_divider)
       tval = tval / comp->ctx->test_timer_divider;
 
     NR_ASYNC_TIMER_SET(tval, nr_ice_component_consent_timeout_cb, comp,
@@ -1239,16 +1252,21 @@ static void nr_ice_component_refresh_con
           nr_ice_component_consent_failed(comp);
         }
         break;
       case NR_STUN_CLIENT_STATE_DONE:
         r_log(LOG_ICE, LOG_INFO, "ICE(%s)/STREAM(%s)/COMP(%d): Consent refreshed",
               comp->ctx->label, comp->stream->label, comp->component_id);
         nr_ice_component_consent_refreshed(comp);
         break;
+      case NR_STUN_CLIENT_STATE_TIMED_OUT:
+        r_log(LOG_ICE, LOG_INFO, "ICE(%s)/STREAM(%s)/COMP(%d): A single consent refresh request timed out",
+              comp->ctx->label, comp->stream->label, comp->component_id);
+        nr_ice_component_consent_request_timed_out(comp);
+        break;
       default:
         break;
     }
   }
 
 int nr_ice_component_refresh_consent(nr_stun_client_ctx *ctx, NR_async_cb finished_cb, void *cb_arg)
   {
     int r,_status;
@@ -1258,16 +1276,33 @@ int nr_ice_component_refresh_consent(nr_
     if (r=nr_stun_client_start(ctx, NR_ICE_CLIENT_MODE_BINDING_REQUEST, finished_cb, cb_arg))
       ABORT(r);
 
     _status=0;
   abort:
     return(_status);
   }
 
+void nr_ice_component_consent_calc_consent_timer(nr_ice_component *comp)
+  {
+    uint16_t trange, trand, tval;
+
+    trange = NR_ICE_CONSENT_TIMER_DEFAULT * 20 / 100;
+    tval = NR_ICE_CONSENT_TIMER_DEFAULT - trange;
+    if (!nr_crypto_random_bytes((UCHAR*)&trand, sizeof(trand)))
+      tval += (trand % (trange * 2));
+
+    if (comp->ctx->test_timer_divider)
+      tval = tval / comp->ctx->test_timer_divider;
+
+    /* The timeout of the transaction is the maximum time until we send the
+     * next consent request. */
+    comp->consent_ctx->maximum_transmits_timeout_ms = tval;
+  }
+
 static void nr_ice_component_consent_timer_cb(NR_SOCKET s, int how, void *cb_arg)
   {
     nr_ice_component *comp=cb_arg;
     int r;
 
     comp->consent_timer = 0;
 
     comp->consent_ctx->params.ice_binding_request.username =
@@ -1277,16 +1312,18 @@ static void nr_ice_component_consent_tim
     comp->consent_ctx->params.ice_binding_request.control =
       comp->stream->pctx->controlling?
       NR_ICE_CONTROLLING:NR_ICE_CONTROLLED;
     comp->consent_ctx->params.ice_binding_request.tiebreaker =
       comp->stream->pctx->tiebreaker;
     comp->consent_ctx->params.ice_binding_request.priority =
       comp->active->local->priority;
 
+    nr_ice_component_consent_calc_consent_timer(comp);
+
     if (r=nr_ice_component_refresh_consent(comp->consent_ctx,
                                            nr_ice_component_refresh_consent_cb,
                                            comp)) {
       r_log(LOG_ICE,LOG_ERR,"ICE(%s)/STREAM(%s)/COMP(%d): Refresh consent failed with %d",
             comp->ctx->label, comp->stream->label, comp->component_id, r);
       /* In case our attempt to send the refresh binding request reports an
        * error we don't have to wait for timeouts, but declare this connection
        * dead right away. */
@@ -1296,28 +1333,22 @@ static void nr_ice_component_consent_tim
     }
 
     nr_ice_component_consent_schedule_consent_timer(comp);
 
   }
 
 void nr_ice_component_consent_schedule_consent_timer(nr_ice_component *comp)
   {
-    uint16_t trange, trand, tval;
-    void *buf = &trand;
+    if (!comp->can_send) {
+      return;
+    }
 
-    trange = NR_ICE_CONSENT_TIMER_DEFAULT / 100 * 20;
-    tval = NR_ICE_CONSENT_TIMER_DEFAULT - trange;
-    if (!nr_crypto_random_bytes(buf, sizeof(trand)))
-      tval += (trand % (trange * 2));
-
-    if (comp->ctx->test_timer_divider)
-      tval = tval / comp->ctx->test_timer_divider;
-
-    NR_ASYNC_TIMER_SET(tval, nr_ice_component_consent_timer_cb, comp,
+    NR_ASYNC_TIMER_SET(comp->consent_ctx->maximum_transmits_timeout_ms,
+                       nr_ice_component_consent_timer_cb, comp,
                        &comp->consent_timer);
   }
 
 void nr_ice_component_consent_destroy(nr_ice_component *comp)
   {
     if (comp->consent_timer) {
       NR_async_timer_cancel(comp->consent_timer);
       comp->consent_timer = 0;
@@ -1344,28 +1375,25 @@ int nr_ice_component_setup_consent(nr_ic
           comp->ctx->label, comp->stream->label, comp->component_id);
 
     if (r=nr_stun_client_ctx_create("consent", comp->active->local->osock,
                                     &comp->active->remote->addr, 0,
                                     &comp->consent_ctx))
       ABORT(r);
     /* Consent request get send only once. */
     comp->consent_ctx->maximum_transmits = 1;
-    /* The timeout of the transaction is the maximum time until we send the
-     * next consent request.
-     * TODO: set this every time we calculate the new random timeout. */
-    comp->consent_ctx->maximum_transmits_timeout_ms = 6000;
 
     if (r=nr_ice_socket_register_stun_client(comp->active->local->isock,
             comp->consent_ctx, &comp->consent_handle))
       ABORT(r);
 
     comp->can_send = 1;
     nr_ice_component_consent_refreshed(comp);
 
+    nr_ice_component_consent_calc_consent_timer(comp);
     nr_ice_component_consent_schedule_consent_timer(comp);
 
     _status=0;
   abort:
     return(_status);
   }
 
 int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair)
--- a/media/mtransport/third_party/nICEr/src/ice/ice_handler.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_handler.h
@@ -54,26 +54,27 @@ int component_id, nr_ice_cand_pair **pot
      May be called again if the nominated pair changes due to
      ICE instability. TODO: !ekr! think about this
   */
   int (*stream_ready)(void *obj, nr_ice_media_stream *stream);
 
   /* This media stream has failed */
   int (*stream_failed)(void *obj, nr_ice_media_stream *stream);
 
-  /* ICE is completed for this peer ctx
-     if need_update is nonzero, a SIP update is required
-   */
-  int (*ice_completed)(void *obj, nr_ice_peer_ctx *pctx);
+  /* ICE is connected for this peer ctx */
+  int (*ice_connected)(void *obj, nr_ice_peer_ctx *pctx);
 
   /* A message was delivered to us */
   int (*msg_recvd)(void *obj, nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, int component_id, UCHAR *msg, int len);
 
   /* ICE has started checking. */
   int (*ice_checking)(void *obj, nr_ice_peer_ctx *pctx);
+
+  /* ICE detected a (temporary?) disconnect. */
+  int (*ice_disconnected)(void *obj, nr_ice_peer_ctx *pctx);
 } nr_ice_handler_vtbl;
 
 typedef struct nr_ice_handler_ {
   void *obj;
   nr_ice_handler_vtbl *vtbl;
 } nr_ice_handler;
 
 #ifdef __cplusplus
--- a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c
@@ -614,18 +614,18 @@ int nr_ice_media_stream_component_nomina
       NR_async_timer_cancel(stream->timer);
       stream->timer=0;
     }
 
     if (stream->pctx->handler) {
       stream->pctx->handler->vtbl->stream_ready(stream->pctx->handler->obj,stream->local_stream);
     }
 
-    /* Now tell the peer_ctx that we're done */
-    if(r=nr_ice_peer_ctx_check_if_done(stream->pctx))
+    /* Now tell the peer_ctx that we're connected */
+    if(r=nr_ice_peer_ctx_check_if_connected(stream->pctx))
       ABORT(r);
 
   done:
     _status=0;
   abort:
     return(_status);
   }
 
@@ -655,18 +655,18 @@ int nr_ice_media_stream_component_failed
       NR_async_timer_cancel(stream->timer);
       stream->timer=0;
     }
 
     if (stream->pctx->handler) {
       stream->pctx->handler->vtbl->stream_failed(stream->pctx->handler->obj,stream->local_stream);
     }
 
-    /* Now tell the peer_ctx that we're done */
-    if(r=nr_ice_peer_ctx_check_if_done(stream->pctx))
+    /* Now tell the peer_ctx that we're connected */
+    if(r=nr_ice_peer_ctx_check_if_connected(stream->pctx))
       ABORT(r);
 
     _status=0;
   abort:
     return(_status);
   }
 
 int nr_ice_media_stream_get_best_candidate(nr_ice_media_stream *str, int component, nr_ice_candidate **candp)
--- a/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c
@@ -461,17 +461,17 @@ int nr_ice_peer_ctx_disable_component(nr
     return(_status);
   }
 
 static void nr_ice_peer_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg)
   {
     nr_ice_peer_ctx *pctx=cb_arg;
     nr_ice_media_stream *str1,*str2;
 
-    NR_async_timer_cancel(pctx->done_cb_timer);
+    NR_async_timer_cancel(pctx->connected_cb_timer);
     RFREE(pctx->label);
     RFREE(pctx->peer_ufrag);
     RFREE(pctx->peer_pwd);
 
     STAILQ_FOREACH_SAFE(str1, &pctx->peer_streams, entry, str2){
       STAILQ_REMOVE(&pctx->peer_streams,str1,nr_ice_media_stream_,entry);
       nr_ice_media_stream_destroy(&str1);
     }
@@ -522,27 +522,27 @@ int nr_ice_peer_ctx_start_checks(nr_ice_
 */
 int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first)
   {
     int r,_status;
     nr_ice_media_stream *stream;
     int started = 0;
 
     /* Might have added some streams */
-    pctx->reported_done = 0;
-    NR_async_timer_cancel(pctx->done_cb_timer);
-    pctx->done_cb_timer = 0;
+    pctx->reported_connected = 0;
+    NR_async_timer_cancel(pctx->connected_cb_timer);
+    pctx->connected_cb_timer = 0;
     pctx->checks_started = 0;
 
-    if((r=nr_ice_peer_ctx_check_if_done(pctx))) {
-      r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) initial done check failed",pctx->ctx->label,pctx->label);
+    if((r=nr_ice_peer_ctx_check_if_connected(pctx))) {
+      r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) initial connected check failed",pctx->ctx->label,pctx->label);
       ABORT(r);
     }
 
-    if (pctx->reported_done) {
+    if (pctx->reported_connected) {
       r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) in %s all streams were done",pctx->ctx->label,pctx->label,__FUNCTION__);
       return (0);
     }
 
     stream=STAILQ_FIRST(&pctx->peer_streams);
     if(!stream)
       ABORT(R_FAILED);
 
@@ -643,31 +643,43 @@ int nr_ice_peer_ctx_dump_state(nr_ice_pe
     fprintf(out,"==========================================\n");
 
     _status=0;
   abort:
     return(_status);
   }
 #endif
 
-static void nr_ice_peer_ctx_fire_done(NR_SOCKET s, int how, void *cb_arg)
+void nr_ice_peer_ctx_disconnected(nr_ice_peer_ctx *pctx)
+  {
+    if (pctx->handler && pctx->handler->vtbl->ice_disconnected) {
+      pctx->handler->vtbl->ice_disconnected(pctx->handler->obj, pctx);
+    }
+  }
+
+void nr_ice_peer_ctx_connected(nr_ice_peer_ctx *pctx)
+  {
+    /* Fire the handler callback to say we're done */
+    if (pctx->handler) {
+      pctx->handler->vtbl->ice_connected(pctx->handler->obj, pctx);
+    }
+  }
+
+static void nr_ice_peer_ctx_fire_connected(NR_SOCKET s, int how, void *cb_arg)
   {
     nr_ice_peer_ctx *pctx=cb_arg;
 
-    pctx->done_cb_timer=0;
+    pctx->connected_cb_timer=0;
 
-    /* Fire the handler callback to say we're done */
-    if (pctx->handler) {
-      pctx->handler->vtbl->ice_completed(pctx->handler->obj, pctx);
-    }
+    nr_ice_peer_ctx_connected(pctx);
   }
 
 /* Examine all the streams to see if we're
-   maybe miraculously done */
-int nr_ice_peer_ctx_check_if_done(nr_ice_peer_ctx *pctx)
+   maybe miraculously connected */
+int nr_ice_peer_ctx_check_if_connected(nr_ice_peer_ctx *pctx)
   {
     int _status;
     nr_ice_media_stream *str;
     int failed=0;
     int succeeded=0;
 
     str=STAILQ_FIRST(&pctx->peer_streams);
     while(str){
@@ -684,23 +696,23 @@ int nr_ice_peer_ctx_check_if_done(nr_ice
     }
 
     if(str)
       goto done;  /* Something isn't done */
 
     /* OK, we're finished, one way or another */
     r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): all checks completed success=%d fail=%d",pctx->label,succeeded,failed);
 
-    /* Schedule a done notification for the first done event.
+    /* Schedule a connected notification for the first connected event.
        IMPORTANT: This is done in a callback because we expect destructors
        of various kinds to be fired from here */
-    if (!pctx->reported_done) {
-      pctx->reported_done = 1;
-      assert(!pctx->done_cb_timer);
-      NR_ASYNC_TIMER_SET(0,nr_ice_peer_ctx_fire_done,pctx,&pctx->done_cb_timer);
+    if (!pctx->reported_connected) {
+      pctx->reported_connected = 1;
+      assert(!pctx->connected_cb_timer);
+      NR_ASYNC_TIMER_SET(0,nr_ice_peer_ctx_fire_connected,pctx,&pctx->connected_cb_timer);
     }
 
   done:
     _status=0;
 //  abort:
     return(_status);
   }
 
--- a/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.h
@@ -57,18 +57,18 @@ struct nr_ice_peer_ctx_ {
   int peer_lite;
   int peer_ice_mismatch;
 
   nr_ice_media_stream_head peer_streams;
   int active_streams;
   int waiting_pairs;
   UCHAR checks_started;
 
-  void *done_cb_timer;
-  UCHAR reported_done;
+  void *connected_cb_timer;
+  UCHAR reported_connected;
   void *trickle_grace_period_timer;
 
   STAILQ_ENTRY(nr_ice_peer_ctx_) entry;
 };
 
 typedef STAILQ_HEAD(nr_ice_peer_ctx_head_, nr_ice_peer_ctx_) nr_ice_peer_ctx_head;
 
 int nr_ice_peer_ctx_create(nr_ice_ctx *ctx, nr_ice_handler *handler,char *label, nr_ice_peer_ctx **pctxp);
@@ -78,19 +78,21 @@ int nr_ice_peer_ctx_find_pstream(nr_ice_
 int nr_ice_peer_ctx_remove_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream **pstreamp);
 int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *cand);
 
 int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx);
 int nr_ice_peer_ctx_parse_global_attributes(nr_ice_peer_ctx *pctx, char **attrs, int attr_ct);
 int nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx *pctx);
 int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first);
 void nr_ice_peer_ctx_stream_started_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
+void nr_ice_peer_ctx_disconnected(nr_ice_peer_ctx *pctx);
+void nr_ice_peer_ctx_connected(nr_ice_peer_ctx *pctx);
 int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out);
 int nr_ice_peer_ctx_log_state(nr_ice_peer_ctx *pctx);
-int nr_ice_peer_ctx_check_if_done(nr_ice_peer_ctx *pctx);
+int nr_ice_peer_ctx_check_if_connected(nr_ice_peer_ctx *pctx);
 int nr_ice_peer_ctx_find_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component_id, nr_ice_component **compp);
 int nr_ice_peer_ctx_deliver_packet_maybe(nr_ice_peer_ctx *pctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len);
 int nr_ice_peer_ctx_disable_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, int component_id);
 int nr_ice_peer_ctx_pair_new_trickle_candidate(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx, nr_ice_candidate *cand);
 void nr_ice_peer_ctx_switch_controlling_role(nr_ice_peer_ctx *pctx);
 
 #ifdef __cplusplus
 }
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -3288,20 +3288,26 @@ PeerConnectionImpl::GetName()
 
 static mozilla::dom::PCImplIceConnectionState
 toDomIceConnectionState(NrIceCtx::ConnectionState state) {
   switch (state) {
     case NrIceCtx::ICE_CTX_INIT:
       return PCImplIceConnectionState::New;
     case NrIceCtx::ICE_CTX_CHECKING:
       return PCImplIceConnectionState::Checking;
-    case NrIceCtx::ICE_CTX_OPEN:
+    case NrIceCtx::ICE_CTX_CONNECTED:
       return PCImplIceConnectionState::Connected;
+    case NrIceCtx::ICE_CTX_COMPLETED:
+      return PCImplIceConnectionState::Completed;
     case NrIceCtx::ICE_CTX_FAILED:
       return PCImplIceConnectionState::Failed;
+    case NrIceCtx::ICE_CTX_DISCONNECTED:
+      return PCImplIceConnectionState::Disconnected;
+    case NrIceCtx::ICE_CTX_CLOSED:
+      return PCImplIceConnectionState::Closed;
   }
   MOZ_CRASH();
 }
 
 static mozilla::dom::PCImplIceGatheringState
 toDomIceGatheringState(NrIceCtx::GatheringState state) {
   switch (state) {
     case NrIceCtx::ICE_CTX_GATHER_INIT:
@@ -3396,18 +3402,17 @@ static bool isDone(PCImplIceConnectionSt
 }
 
 static bool isSucceeded(PCImplIceConnectionState state) {
   return state == PCImplIceConnectionState::Connected ||
          state == PCImplIceConnectionState::Completed;
 }
 
 static bool isFailed(PCImplIceConnectionState state) {
-  return state == PCImplIceConnectionState::Failed ||
-         state == PCImplIceConnectionState::Disconnected;
+  return state == PCImplIceConnectionState::Failed;
 }
 #endif
 
 void PeerConnectionImpl::IceConnectionStateChange(
     NrIceCtx* ctx,
     NrIceCtx::ConnectionState state) {
   PC_AUTO_ENTER_API_CALL_VOID_RETURN(false);