In quic, do not arm pto timer with no packets in flight if handshake packet gets received by peer. affecting client only. not protected.

PiperOrigin-RevId: 321619242
Change-Id: If85c601a6d033d45658546c3b6ac87b8e908367f
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index 948531a..2848137 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -104,6 +104,7 @@
       pto_exponential_backoff_start_point_(0),
       pto_rttvar_multiplier_(4),
       num_tlp_timeout_ptos_(0),
+      handshake_packet_acked_(false),
       one_rtt_packet_acked_(false),
       one_rtt_packet_sent_(false),
       first_pto_srtt_multiplier_(0),
@@ -1053,12 +1054,7 @@
 
 const QuicTime QuicSentPacketManager::GetRetransmissionTime() const {
   if (!unacked_packets_.HasInFlightPackets() &&
-      (!handshake_mode_disabled_ || handshake_finished_ ||
-       unacked_packets_.perspective() == Perspective::IS_SERVER)) {
-    // Do not set the timer if there is nothing in flight. However, to avoid
-    // handshake deadlock due to anti-amplification limit, client needs to set
-    // PTO timer when the handshake is not confirmed even there is nothing in
-    // flight.
+      PeerCompletedAddressValidation()) {
     return QuicTime::Zero();
   }
   if (pending_timer_transmission_count_ > 0) {
@@ -1417,6 +1413,9 @@
       return PACKETS_ACKED_IN_WRONG_PACKET_NUMBER_SPACE;
     }
     last_ack_frame_.packets.Add(acked_packet.packet_number);
+    if (info->encryption_level == ENCRYPTION_HANDSHAKE) {
+      handshake_packet_acked_ = true;
+    }
     if (info->encryption_level == ENCRYPTION_FORWARD_SECURE) {
       one_rtt_packet_acked_ = true;
     }
@@ -1555,5 +1554,16 @@
   return total_delay;
 }
 
+bool QuicSentPacketManager::PeerCompletedAddressValidation() const {
+  if (unacked_packets_.perspective() == Perspective::IS_SERVER ||
+      !handshake_mode_disabled_) {
+    return true;
+  }
+
+  // To avoid handshake deadlock due to anti-amplification limit, client needs
+  // to set PTO timer until server successfully processed any HANDSHAKE packet.
+  return handshake_finished_ || handshake_packet_acked_;
+}
+
 #undef ENDPOINT  // undef for jumbo builds
 }  // namespace quic
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index 4d76f5c..310cd1a 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -529,6 +529,10 @@
   QuicTime::Delta GetNConsecutiveRetransmissionTimeoutDelay(
       int num_timeouts) const;
 
+  // Returns true if peer has finished address validation, such that
+  // retransmission timer is not armed if there is no packets in flight.
+  bool PeerCompletedAddressValidation() const;
+
   // Newly serialized retransmittable packets are added to this map, which
   // contains owning pointers to any contained frames.  If a packet is
   // retransmitted, this map will contain entries for both the old and the new
@@ -653,6 +657,9 @@
   // Number of PTOs similar to TLPs.
   size_t num_tlp_timeout_ptos_;
 
+  // True if any ENCRYPTION_HANDSHAKE packet gets acknowledged.
+  bool handshake_packet_acked_;
+
   // True if any 1-RTT packet gets acknowledged.
   bool one_rtt_packet_acked_;
 
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index a6c8338..2ce7529 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -2765,6 +2765,19 @@
   // Fire PTO.
   EXPECT_EQ(QuicSentPacketManager::PTO_MODE,
             manager_.OnRetransmissionTimeout());
+  // Send handshake packet.
+  SendDataPacket(2, ENCRYPTION_HANDSHAKE);
+  // Ack packet 2.
+  ExpectAck(2);
+  manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(),
+                           clock_.Now());
+  manager_.OnAckRange(QuicPacketNumber(2), QuicPacketNumber(3));
+  EXPECT_EQ(PACKETS_NEWLY_ACKED,
+            manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(2),
+                                   ENCRYPTION_HANDSHAKE));
+  // Verify retransmission timeout is zero because server has successfully
+  // processed HANDSHAKE packet.
+  EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
 }
 
 TEST_F(QuicSentPacketManagerTest, DisableHandshakeModeServer) {