Add QuicFramer::PotentialPeerKeyUpdateAttemptCount() method.

Returns the count of packets received that appeared to attempt a key
update but failed decryption which have been received since the last
successfully decrypted packet.

PiperOrigin-RevId: 338710571
Change-Id: Iaae901269d6bffaf441a31a4a51540aa94c84b73
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 71bb8be..a7f63dd 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -3721,6 +3721,10 @@
           GetLargestAckedPacket() < lowest_packet_sent_in_current_key_phase_);
 }
 
+QuicPacketCount QuicConnection::PotentialPeerKeyUpdateAttemptCount() const {
+  return framer_.PotentialPeerKeyUpdateAttemptCount();
+}
+
 bool QuicConnection::InitiateKeyUpdate(KeyUpdateReason reason) {
   QUIC_DLOG(INFO) << ENDPOINT << "InitiateKeyUpdate";
   if (!IsKeyUpdateAllowed()) {
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index a3da511..31988d2 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -828,6 +828,11 @@
   // none of these packets have been acked.
   bool HaveSentPacketsInCurrentKeyPhaseButNoneAcked() const;
 
+  // Returns the count of packets received that appeared to attempt a key
+  // update but failed decryption that have been received since the last
+  // successfully decrypted packet.
+  QuicPacketCount PotentialPeerKeyUpdateAttemptCount() const;
+
   // Increment the key phase. It is a bug to call this when IsKeyUpdateAllowed()
   // is false. Returns false on error.
   bool InitiateKeyUpdate(KeyUpdateReason reason);
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index 39be55e..cfa56c2 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -414,6 +414,7 @@
       last_timestamp_(QuicTime::Delta::Zero()),
       support_key_update_for_connection_(false),
       current_key_phase_bit_(false),
+      potential_peer_key_update_attempt_count_(0),
       first_sending_packet_number_(FirstSendingPacketNumber()),
       data_producer_(nullptr),
       infer_packet_header_type_from_version_(perspective ==
@@ -4282,6 +4283,10 @@
   return true;
 }
 
+QuicPacketCount QuicFramer::PotentialPeerKeyUpdateAttemptCount() const {
+  return potential_peer_key_update_attempt_count_;
+}
+
 const QuicDecrypter* QuicFramer::GetDecrypter(EncryptionLevel level) const {
   DCHECK(version_.KnowsWhichDecrypterToUse());
   return decrypter_[level].get();
@@ -4717,6 +4722,7 @@
           QUIC_DVLOG(1) << ENDPOINT << "packet " << header.packet_number
                         << " attempt_key_update=true";
           attempt_key_update = true;
+          potential_peer_key_update_attempt_count_++;
           decrypter = next_decrypter_.get();
         } else {
           if (previous_decrypter_) {
@@ -4752,6 +4758,7 @@
   if (success) {
     visitor_->OnDecryptedPacket(level);
     *decrypted_level = level;
+    potential_peer_key_update_attempt_count_ = 0;
     if (attempt_key_update) {
       if (!DoKeyUpdate(KeyUpdateReason::kRemote)) {
         set_detailed_error("Key update failed due to internal error");
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h
index 5d9bf46..86c8882 100644
--- a/quic/core/quic_framer.h
+++ b/quic/core/quic_framer.h
@@ -547,6 +547,10 @@
   void DiscardPreviousOneRttKeys();
   // Update the key phase.
   bool DoKeyUpdate(KeyUpdateReason reason);
+  // Returns the count of packets received that appeared to attempt a key
+  // update but failed decryption which have been received since the last
+  // successfully decrypted packet.
+  QuicPacketCount PotentialPeerKeyUpdateAttemptCount() const;
 
   const QuicDecrypter* GetDecrypter(EncryptionLevel level) const;
   const QuicDecrypter* decrypter() const;
@@ -1104,6 +1108,10 @@
   // locally initiated key update but before the first packet from the peer in
   // the new key phase is received.
   QuicPacketNumber current_key_phase_first_received_packet_number_;
+  // Counts the number of packets received that might have been failed key
+  // update attempts. Reset to zero every time a packet is successfully
+  // decrypted.
+  QuicPacketCount potential_peer_key_update_attempt_count_;
   // Decrypter for the previous key phase. Will be null if in the first key
   // phase or previous keys have been discarded.
   std::unique_ptr<QuicDecrypter> previous_decrypter_;
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index ab61814..2fbab61 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -14925,6 +14925,7 @@
   EXPECT_EQ(0u, visitor_.key_update_count());
   EXPECT_EQ(0, visitor_.derive_next_key_count_);
   EXPECT_EQ(1, visitor_.decrypted_first_packet_in_key_phase_count_);
+  EXPECT_EQ(0u, framer_.PotentialPeerKeyUpdateAttemptCount());
 
   header.packet_number += 1;
   QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
@@ -14940,6 +14941,7 @@
   EXPECT_EQ(0u, visitor_.key_update_count());
   EXPECT_EQ(1, visitor_.derive_next_key_count_);
   EXPECT_EQ(1, visitor_.decrypted_first_packet_in_key_phase_count_);
+  EXPECT_EQ(1u, framer_.PotentialPeerKeyUpdateAttemptCount());
 
   header.packet_number += 1;
   QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
@@ -14954,6 +14956,7 @@
   EXPECT_EQ(0u, visitor_.key_update_count());
   EXPECT_EQ(1, visitor_.derive_next_key_count_);
   EXPECT_EQ(1, visitor_.decrypted_first_packet_in_key_phase_count_);
+  EXPECT_EQ(2u, framer_.PotentialPeerKeyUpdateAttemptCount());
 
   header.packet_number += 1;
   QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
@@ -14968,6 +14971,22 @@
   EXPECT_EQ(0u, visitor_.key_update_count());
   EXPECT_EQ(1, visitor_.derive_next_key_count_);
   EXPECT_EQ(1, visitor_.decrypted_first_packet_in_key_phase_count_);
+  EXPECT_EQ(2u, framer_.PotentialPeerKeyUpdateAttemptCount());
+
+  header.packet_number += 1;
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
+  data = BuildDataPacket(header, frames);
+  ASSERT_TRUE(data != nullptr);
+  encrypted = EncryptPacketWithTagAndPhase(*data, 0, false);
+  ASSERT_TRUE(encrypted);
+  QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER);
+  // Packet with phase=0 and key=0, should process and reset
+  // potential_peer_key_update_attempt_count_.
+  EXPECT_TRUE(framer_.ProcessPacket(*encrypted));
+  EXPECT_EQ(0u, visitor_.key_update_count());
+  EXPECT_EQ(1, visitor_.derive_next_key_count_);
+  EXPECT_EQ(1, visitor_.decrypted_first_packet_in_key_phase_count_);
+  EXPECT_EQ(0u, framer_.PotentialPeerKeyUpdateAttemptCount());
 }
 
 TEST_P(QuicFramerTest, KeyUpdateReceivedWhenNotEnabled) {