gfe-relnote: In QUIC, when arming PTO timer, ignore application data until handshake completes rather than handshake confirmed. Not affecting prod, not protected.

Also refactor GetEarliestPacketSentTimeForPto to make the code cleaner.

PiperOrigin-RevId: 296039749
Change-Id: I6ed4507a5126cc641dc09259830f2b0ce2f1012f
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index 92e6e84..e4f46d8 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -104,7 +104,8 @@
       pto_exponential_backoff_start_point_(0),
       pto_rttvar_multiplier_(4),
       num_tlp_timeout_ptos_(0),
-      one_rtt_packet_acked_(false) {
+      one_rtt_packet_acked_(false),
+      one_rtt_packet_sent_(false) {
   SetSendAlgorithm(congestion_control_type);
 }
 
@@ -475,24 +476,33 @@
   for (int8_t i = 0; i < NUM_PACKET_NUMBER_SPACES; ++i) {
     const QuicTime sent_time = unacked_packets_.GetLastInFlightPacketSentTime(
         static_cast<PacketNumberSpace>(i));
-    if (!earliest_sent_time.IsInitialized()) {
-      earliest_sent_time = sent_time;
-      *packet_number_space = static_cast<PacketNumberSpace>(i);
+    if (!ShouldArmPtoForApplicationData() && i == APPLICATION_DATA) {
       continue;
     }
-    if (i == APPLICATION_DATA) {
-      // Not arming PTO for application data to prioritize the completion of
-      // handshake.
-      break;
+    if (!sent_time.IsInitialized() || (earliest_sent_time.IsInitialized() &&
+                                       earliest_sent_time <= sent_time)) {
+      continue;
     }
-    if (sent_time.IsInitialized()) {
-      earliest_sent_time = std::min(earliest_sent_time, sent_time);
-      *packet_number_space = static_cast<PacketNumberSpace>(i);
-    }
+    earliest_sent_time = sent_time;
+    *packet_number_space = static_cast<PacketNumberSpace>(i);
   }
+
   return earliest_sent_time;
 }
 
+bool QuicSentPacketManager::ShouldArmPtoForApplicationData() const {
+  DCHECK(supports_multiple_packet_number_spaces());
+  // Application data must be ignored before handshake completes (1-RTT key
+  // is available). Not arming PTO for application data to prioritize the
+  // completion of handshake. On the server side, handshake_finished_
+  // indicates handshake complete (and confirmed). On the client side,
+  // one_rtt_packet_sent_ indicates handshake complete (while handshake
+  // confirmation will happen later).
+  return handshake_finished_ ||
+         (unacked_packets_.perspective() == Perspective::IS_CLIENT &&
+          one_rtt_packet_sent_);
+}
+
 void QuicSentPacketManager::MarkForRetransmission(
     QuicPacketNumber packet_number,
     TransmissionType transmission_type) {
@@ -635,6 +645,10 @@
         serialized_packet->encrypted_length, has_retransmittable_data);
   }
 
+  if (serialized_packet->encryption_level == ENCRYPTION_FORWARD_SECURE) {
+    one_rtt_packet_sent_ = true;
+  }
+
   unacked_packets_.AddSentPacket(serialized_packet, transmission_type,
                                  sent_time, in_flight);
   // Reset the retransmission timer anytime a pending packet is sent.
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index 61631c2..d8fc069 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -515,6 +515,10 @@
   QuicTime GetEarliestPacketSentTimeForPto(
       PacketNumberSpace* packet_number_space) const;
 
+  // Returns true if application data should be used to arm PTO. Only used when
+  // multiple packet number space is enabled.
+  bool ShouldArmPtoForApplicationData() 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
@@ -641,6 +645,9 @@
 
   // True if any 1-RTT packet gets acknowledged.
   bool one_rtt_packet_acked_;
+
+  // True if any 1-RTT packet gets sent.
+  bool one_rtt_packet_sent_;
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index 2449cd6..08e918e 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -3210,6 +3210,7 @@
   rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
                        QuicTime::Delta::Zero(), QuicTime::Zero());
   QuicTime::Delta srtt = rtt_stats->smoothed_rtt();
+  QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT);
 
   // Send packet 1.
   SendDataPacket(1, ENCRYPTION_INITIAL);
@@ -3247,10 +3248,9 @@
   EXPECT_EQ(packet3_sent_time + expected_pto_delay * 2,
             manager_.GetRetransmissionTime());
 
-  // Send packet 4 in application data.
+  // Send packet 4 in application data with 0-RTT.
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
-  SendDataPacket(4, ENCRYPTION_FORWARD_SECURE);
-  const QuicTime packet4_sent_time = clock_.Now();
+  SendDataPacket(4, ENCRYPTION_ZERO_RTT);
   // Verify PTO timeout is still based on packet 3.
   EXPECT_EQ(packet3_sent_time + expected_pto_delay * 2,
             manager_.GetRetransmissionTime());
@@ -3258,14 +3258,31 @@
   // Send packet 5 in handshake.
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
   SendDataPacket(5, ENCRYPTION_HANDSHAKE);
-  // Verify PTO timeout is now based on packet 5.
+  const QuicTime packet5_sent_time = clock_.Now();
+  // Verify PTO timeout is now based on packet 5 because packet 4 should be
+  // ignored.
   EXPECT_EQ(clock_.Now() + expected_pto_delay * 2,
             manager_.GetRetransmissionTime());
 
+  // Send packet 6 in 1-RTT.
+  clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+  SendDataPacket(6, ENCRYPTION_FORWARD_SECURE);
+  const QuicTime packet6_sent_time = clock_.Now();
+  // Verify PTO timeout is now based on packet 5.
+  EXPECT_EQ(packet5_sent_time + expected_pto_delay * 2,
+            manager_.GetRetransmissionTime());
+
+  // Send packet 7 in handshake.
+  clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+  SendDataPacket(7, ENCRYPTION_HANDSHAKE);
+  // Verify PTO timeout is now based on packet 6.
+  EXPECT_EQ(packet6_sent_time + expected_pto_delay * 2,
+            manager_.GetRetransmissionTime());
+
   // Neuter handshake key.
   manager_.SetHandshakeConfirmed();
-  // Verify PTO timeout is now based on packet 4.
-  EXPECT_EQ(packet4_sent_time + expected_pto_delay * 2,
+  // Verify PTO timeout remains unchanged.
+  EXPECT_EQ(packet6_sent_time + expected_pto_delay * 2,
             manager_.GetRetransmissionTime());
 }
 
@@ -3294,22 +3311,36 @@
   // Send packet 2 in handshake.
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
   SendDataPacket(2, ENCRYPTION_HANDSHAKE);
+  const QuicTime packet2_sent_time = clock_.Now();
   // Verify PTO timeout is still based on packet 1.
   EXPECT_EQ(packet1_sent_time + expected_pto_delay,
             manager_.GetRetransmissionTime());
 
-  // Discard both initial and handshake keys.
+  // Discard initial keys.
   EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
   manager_.NeuterUnencryptedPackets();
-  manager_.SetHandshakeConfirmed();
-  EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
 
-  // Send packet 3 in application data.
+  // Send packet 3 in 1-RTT.
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
   SendDataPacket(3, ENCRYPTION_FORWARD_SECURE);
-  // Verify PTO timeout is correctly set.
+  // Verify PTO timeout is based on packet 2.
+  const QuicTime packet3_sent_time = clock_.Now();
+  EXPECT_EQ(packet2_sent_time + expected_pto_delay,
+            manager_.GetRetransmissionTime());
+
+  // Send packet 4 in handshake.
+  clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+  SendDataPacket(4, ENCRYPTION_HANDSHAKE);
+  // Verify PTO timeout is based on packet 4 as application data is ignored.
   EXPECT_EQ(clock_.Now() + expected_pto_delay,
             manager_.GetRetransmissionTime());
+
+  // Discard handshake keys.
+  manager_.SetHandshakeConfirmed();
+  // Verify PTO timeout is now based on packet 3 as handshake is
+  // complete/confirmed.
+  EXPECT_EQ(packet3_sent_time + expected_pto_delay,
+            manager_.GetRetransmissionTime());
 }
 
 // Regresstion test for b/148841700.