Move all of the individual alarms in QuicConnection into a new holder class.

This is a first step towards replacing individual alarms with one big connection-wide alarm.

This CL only covers alarms in the QuicConnection itself; some of the adjacent alarms will be moved in the follow-up CL.

PiperOrigin-RevId: 628392148
diff --git a/build/source_list.bzl b/build/source_list.bzl
index a28d283..67a610a 100644
--- a/build/source_list.bzl
+++ b/build/source_list.bzl
@@ -282,6 +282,7 @@
     "quic/core/quic_coalesced_packet.h",
     "quic/core/quic_config.h",
     "quic/core/quic_connection.h",
+    "quic/core/quic_connection_alarms.h",
     "quic/core/quic_connection_context.h",
     "quic/core/quic_connection_id.h",
     "quic/core/quic_connection_id_manager.h",
@@ -613,6 +614,7 @@
     "quic/core/quic_coalesced_packet.cc",
     "quic/core/quic_config.cc",
     "quic/core/quic_connection.cc",
+    "quic/core/quic_connection_alarms.cc",
     "quic/core/quic_connection_context.cc",
     "quic/core/quic_connection_id.cc",
     "quic/core/quic_connection_id_manager.cc",
diff --git a/build/source_list.gni b/build/source_list.gni
index 5abdeab..afc2300 100644
--- a/build/source_list.gni
+++ b/build/source_list.gni
@@ -282,6 +282,7 @@
     "src/quiche/quic/core/quic_coalesced_packet.h",
     "src/quiche/quic/core/quic_config.h",
     "src/quiche/quic/core/quic_connection.h",
+    "src/quiche/quic/core/quic_connection_alarms.h",
     "src/quiche/quic/core/quic_connection_context.h",
     "src/quiche/quic/core/quic_connection_id.h",
     "src/quiche/quic/core/quic_connection_id_manager.h",
@@ -613,6 +614,7 @@
     "src/quiche/quic/core/quic_coalesced_packet.cc",
     "src/quiche/quic/core/quic_config.cc",
     "src/quiche/quic/core/quic_connection.cc",
+    "src/quiche/quic/core/quic_connection_alarms.cc",
     "src/quiche/quic/core/quic_connection_context.cc",
     "src/quiche/quic/core/quic_connection_id.cc",
     "src/quiche/quic/core/quic_connection_id_manager.cc",
diff --git a/build/source_list.json b/build/source_list.json
index 38d2b75..b49530e 100644
--- a/build/source_list.json
+++ b/build/source_list.json
@@ -281,6 +281,7 @@
     "quiche/quic/core/quic_coalesced_packet.h",
     "quiche/quic/core/quic_config.h",
     "quiche/quic/core/quic_connection.h",
+    "quiche/quic/core/quic_connection_alarms.h",
     "quiche/quic/core/quic_connection_context.h",
     "quiche/quic/core/quic_connection_id.h",
     "quiche/quic/core/quic_connection_id_manager.h",
@@ -612,6 +613,7 @@
     "quiche/quic/core/quic_coalesced_packet.cc",
     "quiche/quic/core/quic_config.cc",
     "quiche/quic/core/quic_connection.cc",
+    "quiche/quic/core/quic_connection_alarms.cc",
     "quiche/quic/core/quic_connection_context.cc",
     "quiche/quic/core/quic_connection_id.cc",
     "quiche/quic/core/quic_connection_id_manager.cc",
diff --git a/quiche/quic/core/http/end_to_end_test.cc b/quiche/quic/core/http/end_to_end_test.cc
index b644d0d..d60bede 100644
--- a/quiche/quic/core/http/end_to_end_test.cc
+++ b/quiche/quic/core/http/end_to_end_test.cc
@@ -2338,9 +2338,7 @@
   // connection is still flow control blocked.
   session->connection()->OnCanWrite();
 
-  QuicAlarm* send_alarm =
-      QuicConnectionPeer::GetSendAlarm(session->connection());
-  EXPECT_FALSE(send_alarm->IsSet());
+  EXPECT_FALSE(QuicConnectionPeer::GetSendAlarm(session->connection()).IsSet());
 }
 
 TEST_P(EndToEndTest, InvalidStream) {
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index 3b63ce0..f3d696f 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -71,122 +71,6 @@
 // but doesn't allow multiple RTTs of user delay in the hope of using ECN.
 const uint8_t kEcnPtoLimit = 2;
 
-// Base class of all alarms owned by a QuicConnection.
-class QuicConnectionAlarmDelegate : public QuicAlarm::Delegate {
- public:
-  explicit QuicConnectionAlarmDelegate(QuicConnection* connection)
-      : connection_(connection) {}
-  QuicConnectionAlarmDelegate(const QuicConnectionAlarmDelegate&) = delete;
-  QuicConnectionAlarmDelegate& operator=(const QuicConnectionAlarmDelegate&) =
-      delete;
-
-  QuicConnectionContext* GetConnectionContext() override {
-    return (connection_ == nullptr) ? nullptr : connection_->context();
-  }
-
- protected:
-  QuicConnection* connection_;
-};
-
-// An alarm that is scheduled to send an ack if a timeout occurs.
-class AckAlarmDelegate : public QuicConnectionAlarmDelegate {
- public:
-  using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
-
-  void OnAlarm() override {
-    QUICHE_DCHECK(connection_->ack_frame_updated());
-    QUICHE_DCHECK(connection_->connected());
-    QuicConnection::ScopedPacketFlusher flusher(connection_);
-    if (connection_->SupportsMultiplePacketNumberSpaces()) {
-      connection_->SendAllPendingAcks();
-    } else {
-      connection_->SendAck();
-    }
-  }
-};
-
-// This alarm will be scheduled any time a data-bearing packet is sent out.
-// When the alarm goes off, the connection checks to see if the oldest packets
-// have been acked, and retransmit them if they have not.
-class RetransmissionAlarmDelegate : public QuicConnectionAlarmDelegate {
- public:
-  using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
-
-  void OnAlarm() override {
-    QUICHE_DCHECK(connection_->connected());
-    connection_->OnRetransmissionTimeout();
-  }
-};
-
-// An alarm that is scheduled when the SentPacketManager requires a delay
-// before sending packets and fires when the packet may be sent.
-class SendAlarmDelegate : public QuicConnectionAlarmDelegate {
- public:
-  using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
-
-  void OnAlarm() override {
-    QUICHE_DCHECK(connection_->connected());
-    connection_->OnSendAlarm();
-  }
-};
-
-class MtuDiscoveryAlarmDelegate : public QuicConnectionAlarmDelegate {
- public:
-  using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
-
-  void OnAlarm() override {
-    QUICHE_DCHECK(connection_->connected());
-    connection_->DiscoverMtu();
-  }
-};
-
-class ProcessUndecryptablePacketsAlarmDelegate
-    : public QuicConnectionAlarmDelegate {
- public:
-  using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
-
-  void OnAlarm() override {
-    QUICHE_DCHECK(connection_->connected());
-    QuicConnection::ScopedPacketFlusher flusher(connection_);
-    connection_->MaybeProcessUndecryptablePackets();
-  }
-};
-
-class DiscardPreviousOneRttKeysAlarmDelegate
-    : public QuicConnectionAlarmDelegate {
- public:
-  using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
-
-  void OnAlarm() override {
-    QUICHE_DCHECK(connection_->connected());
-    connection_->DiscardPreviousOneRttKeys();
-  }
-};
-
-class DiscardZeroRttDecryptionKeysAlarmDelegate
-    : public QuicConnectionAlarmDelegate {
- public:
-  using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
-
-  void OnAlarm() override {
-    QUICHE_DCHECK(connection_->connected());
-    QUIC_DLOG(INFO) << "0-RTT discard alarm fired";
-    connection_->RemoveDecrypter(ENCRYPTION_ZERO_RTT);
-    connection_->RetireOriginalDestinationConnectionId();
-  }
-};
-
-class MultiPortProbingAlarmDelegate : public QuicConnectionAlarmDelegate {
- public:
-  using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
-
-  void OnAlarm() override {
-    QUICHE_DCHECK(connection_->connected());
-    QUIC_DLOG(INFO) << "Alternative path probing alarm fired";
-    connection_->MaybeProbeMultiPortPath();
-  }
-};
-
 // When the clearer goes out of scope, the coalesced packet gets cleared.
 class ScopedCoalescedPacketClearer {
  public:
@@ -295,23 +179,7 @@
       pending_retransmission_alarm_(false),
       defer_send_in_response_to_packets_(false),
       arena_(),
-      ack_alarm_(alarm_factory_->CreateAlarm(arena_.New<AckAlarmDelegate>(this),
-                                             &arena_)),
-      retransmission_alarm_(alarm_factory_->CreateAlarm(
-          arena_.New<RetransmissionAlarmDelegate>(this), &arena_)),
-      send_alarm_(alarm_factory_->CreateAlarm(
-          arena_.New<SendAlarmDelegate>(this), &arena_)),
-      mtu_discovery_alarm_(alarm_factory_->CreateAlarm(
-          arena_.New<MtuDiscoveryAlarmDelegate>(this), &arena_)),
-      process_undecryptable_packets_alarm_(alarm_factory_->CreateAlarm(
-          arena_.New<ProcessUndecryptablePacketsAlarmDelegate>(this), &arena_)),
-      discard_previous_one_rtt_keys_alarm_(alarm_factory_->CreateAlarm(
-          arena_.New<DiscardPreviousOneRttKeysAlarmDelegate>(this), &arena_)),
-      discard_zero_rtt_decryption_keys_alarm_(alarm_factory_->CreateAlarm(
-          arena_.New<DiscardZeroRttDecryptionKeysAlarmDelegate>(this),
-          &arena_)),
-      multi_port_probing_alarm_(alarm_factory_->CreateAlarm(
-          arena_.New<MultiPortProbingAlarmDelegate>(this), &arena_)),
+      alarms_(this, *alarm_factory_, arena_),
       visitor_(nullptr),
       debug_visitor_(nullptr),
       packet_creator_(server_connection_id, &framer_, random_generator_, this),
@@ -1152,7 +1020,7 @@
   }
 }
 
-bool QuicConnection::HasPendingAcks() const { return ack_alarm_->IsSet(); }
+bool QuicConnection::HasPendingAcks() const { return ack_alarm().IsSet(); }
 
 void QuicConnection::OnUserAgentIdKnown(const std::string& /*user_agent_id*/) {
   sent_packet_manager_.OnUserAgentIdKnown();
@@ -1172,7 +1040,7 @@
       // within a short time; the RECOMMENDED time period is three times the
       // Probe Timeout.
       // https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#name-discarding-0-rtt-keys
-      discard_zero_rtt_decryption_keys_alarm_->Set(
+      discard_zero_rtt_decryption_keys_alarm().Set(
           clock_->ApproximateNow() + sent_packet_manager_.GetPtoDelay() * 3);
     }
   }
@@ -1534,8 +1402,8 @@
   // Cancel the send alarm because new packets likely have been acked, which
   // may change the congestion window and/or pacing rate.  Canceling the alarm
   // causes CanWrite to recalculate the next send time.
-  if (send_alarm_->IsSet()) {
-    send_alarm_->Cancel();
+  if (send_alarm().IsSet()) {
+    send_alarm().Cancel();
   }
   if (supports_release_time_) {
     // Update pace time into future because smoothed RTT is likely updated.
@@ -2278,7 +2146,7 @@
   // If another key update triggers while the previous
   // discard_previous_one_rtt_keys_alarm_ hasn't fired yet, cancel it since the
   // old keys would already be discarded.
-  discard_previous_one_rtt_keys_alarm_->Cancel();
+  discard_previous_one_rtt_keys_alarm().Cancel();
 
   visitor_->OnKeyUpdate(reason);
 }
@@ -2292,7 +2160,7 @@
   // Note that this will cause an unnecessary
   // discard_previous_one_rtt_keys_alarm_ on the first packet in the 1RTT
   // encryption level, but this is harmless.
-  discard_previous_one_rtt_keys_alarm_->Set(
+  discard_previous_one_rtt_keys_alarm().Set(
       clock_->ApproximateNow() + sent_packet_manager_.GetPtoDelay() * 3);
 }
 
@@ -2386,32 +2254,32 @@
   // and cancel the alarm temporarily. The rest of this function will ensure
   // the alarm deadline is no later than |max_deadline| when the function exits.
   QuicTime max_deadline = QuicTime::Infinite();
-  if (send_alarm_->IsSet()) {
-    QUIC_DVLOG(1) << "Send alarm already set to " << send_alarm_->deadline();
-    max_deadline = send_alarm_->deadline();
-    send_alarm_->Cancel();
+  if (send_alarm().IsSet()) {
+    QUIC_DVLOG(1) << "Send alarm already set to " << send_alarm().deadline();
+    max_deadline = send_alarm().deadline();
+    send_alarm().Cancel();
   }
 
   if (CanWrite(HAS_RETRANSMITTABLE_DATA)) {
     // Some data can be written immediately. Register for immediate resumption
     // so we'll keep writing after other connections.
-    QUIC_BUG_IF(quic_send_alarm_set_with_data_to_send, send_alarm_->IsSet());
+    QUIC_BUG_IF(quic_send_alarm_set_with_data_to_send, send_alarm().IsSet());
     QUIC_DVLOG(1) << "Immediate send alarm scheduled after processing packet.";
-    send_alarm_->Set(clock_->ApproximateNow() +
+    send_alarm().Set(clock_->ApproximateNow() +
                      sent_packet_manager_.GetDeferredSendAlarmDelay());
     return;
   }
 
-  if (send_alarm_->IsSet()) {
+  if (send_alarm().IsSet()) {
     // Pacing limited: CanWrite returned false, and it has scheduled a send
     // alarm before it returns.
-    if (send_alarm_->deadline() > max_deadline) {
+    if (send_alarm().deadline() > max_deadline) {
       QUIC_DVLOG(1)
           << "Send alarm restored after processing packet. previous deadline:"
           << max_deadline
-          << ", deadline from CanWrite:" << send_alarm_->deadline();
+          << ", deadline from CanWrite:" << send_alarm().deadline();
       // Restore to the previous, earlier deadline.
-      send_alarm_->Update(max_deadline, QuicTime::Delta::Zero());
+      send_alarm().Update(max_deadline, QuicTime::Delta::Zero());
     } else {
       QUIC_DVLOG(1) << "Future send alarm scheduled after processing packet.";
     }
@@ -2420,7 +2288,7 @@
 
   if (max_deadline != QuicTime::Infinite()) {
     QUIC_DVLOG(1) << "Send alarm restored after processing packet.";
-    send_alarm_->Set(max_deadline);
+    send_alarm().Set(max_deadline);
     return;
   }
   // Can not send data due to other reasons: congestion blocked, anti
@@ -2812,11 +2680,11 @@
 
   // After the visitor writes, it may have caused the socket to become write
   // blocked or the congestion manager to prohibit sending, so check again.
-  if (visitor_->WillingAndAbleToWrite() && !send_alarm_->IsSet() &&
+  if (visitor_->WillingAndAbleToWrite() && !send_alarm().IsSet() &&
       CanWrite(HAS_RETRANSMITTABLE_DATA)) {
     // We're not write blocked, but some data wasn't written. Register for
     // 'immediate' resumption so we'll keep writing after other connections.
-    send_alarm_->Set(clock_->ApproximateNow());
+    send_alarm().Set(clock_->ApproximateNow());
   }
 }
 
@@ -3100,7 +2968,7 @@
       // actual MTU is, so there is no need to probe further.
       // TODO(wub): Reduce max packet size to a safe default, or the actual MTU.
       mtu_discoverer_.Disable();
-      mtu_discovery_alarm_->Cancel();
+      mtu_discovery_alarm().Cancel();
       buffered_packets_.pop_front();
       continue;
     }
@@ -3141,7 +3009,7 @@
     // Stop sending ack of initial packet number space.
     uber_received_packet_manager_.ResetAckStates(ENCRYPTION_INITIAL);
     // Re-arm ack alarm.
-    ack_alarm_->Update(uber_received_packet_manager_.GetEarliestAckTimeout(),
+    ack_alarm().Update(uber_received_packet_manager_.GetEarliestAckTimeout(),
                        kAlarmGranularity);
   }
 }
@@ -3296,14 +3164,14 @@
     return true;
   }
   // If the send alarm is set, wait for it to fire.
-  if (send_alarm_->IsSet()) {
+  if (send_alarm().IsSet()) {
     return false;
   }
 
   QuicTime now = clock_->Now();
   QuicTime::Delta delay = sent_packet_manager_.TimeUntilSend(now);
   if (delay.IsInfinite()) {
-    send_alarm_->Cancel();
+    send_alarm().Cancel();
     return false;
   }
 
@@ -3314,7 +3182,7 @@
       return true;
     }
     // Cannot send packet now because delay is too far in the future.
-    send_alarm_->Update(now + delay, kAlarmGranularity);
+    send_alarm().Update(now + delay, kAlarmGranularity);
     QUIC_DVLOG(1) << ENDPOINT << "Delaying sending " << delay.ToMilliseconds()
                   << "ms";
     return false;
@@ -3546,7 +3414,7 @@
                     << " MTU probe packet too big, size:" << encrypted_length
                     << ", long_term_mtu_:" << long_term_mtu_;
       mtu_discoverer_.Disable();
-      mtu_discovery_alarm_->Cancel();
+      mtu_discovery_alarm().Cancel();
       // The write failed, but the writer is not blocked, so return true.
       return true;
     }
@@ -3659,7 +3527,7 @@
       return true;
     }
   }
-  if (in_flight || !retransmission_alarm_->IsSet()) {
+  if (in_flight || !retransmission_alarm().IsSet()) {
     SetRetransmissionAlarm();
   }
   SetPingAlarm();
@@ -3885,7 +3753,7 @@
 
   SetMaxPacketLength(previous_validated_mtu_);
   mtu_discoverer_.Disable();
-  mtu_discovery_alarm_->Cancel();
+  mtu_discovery_alarm().Cancel();
   previous_validated_mtu_ = 0;
   return true;
 }
@@ -4059,14 +3927,14 @@
     // The client should immediately ack the SHLO to confirm the handshake is
     // complete with the server.
     if (perspective_ == Perspective::IS_CLIENT && ack_frame_updated()) {
-      ack_alarm_->Update(clock_->ApproximateNow(), QuicTime::Delta::Zero());
+      ack_alarm().Update(clock_->ApproximateNow(), QuicTime::Delta::Zero());
     }
     return;
   }
   // Stop sending ack of handshake packet number space.
   uber_received_packet_manager_.ResetAckStates(ENCRYPTION_HANDSHAKE);
   // Re-arm ack alarm.
-  ack_alarm_->Update(uber_received_packet_manager_.GetEarliestAckTimeout(),
+  ack_alarm().Update(uber_received_packet_manager_.GetEarliestAckTimeout(),
                      kAlarmGranularity);
   if (!accelerated_server_preferred_address_ &&
       received_server_preferred_address_.IsInitialized()) {
@@ -4281,7 +4149,7 @@
   // and nothing waiting to be sent.
   // This happens if the loss algorithm invokes a timer based loss, but the
   // packet doesn't need to be retransmitted.
-  if (!HasQueuedData() && !retransmission_alarm_->IsSet()) {
+  if (!HasQueuedData() && !retransmission_alarm().IsSet()) {
     SetRetransmissionAlarm();
   }
   if (packet_writer_params_.ecn_codepoint == ECN_NOT_ECT ||
@@ -4349,8 +4217,8 @@
   framer_.SetDecrypter(level, std::move(decrypter));
 
   if (!undecryptable_packets_.empty() &&
-      !process_undecryptable_packets_alarm_->IsSet()) {
-    process_undecryptable_packets_alarm_->Set(clock_->ApproximateNow());
+      !process_undecryptable_packets_alarm().IsSet()) {
+    process_undecryptable_packets_alarm().Set(clock_->ApproximateNow());
   }
 }
 
@@ -4360,8 +4228,8 @@
   framer_.SetAlternativeDecrypter(level, std::move(decrypter), latch_once_used);
 
   if (!undecryptable_packets_.empty() &&
-      !process_undecryptable_packets_alarm_->IsSet()) {
-    process_undecryptable_packets_alarm_->Set(clock_->ApproximateNow());
+      !process_undecryptable_packets_alarm().IsSet()) {
+    process_undecryptable_packets_alarm().Set(clock_->ApproximateNow());
   }
 }
 
@@ -4372,8 +4240,8 @@
   }
   framer_.InstallDecrypter(level, std::move(decrypter));
   if (!undecryptable_packets_.empty() &&
-      !process_undecryptable_packets_alarm_->IsSet()) {
-    process_undecryptable_packets_alarm_->Set(clock_->ApproximateNow());
+      !process_undecryptable_packets_alarm().IsSet()) {
+    process_undecryptable_packets_alarm().Set(clock_->ApproximateNow());
   }
 }
 
@@ -4437,7 +4305,7 @@
 }
 
 void QuicConnection::MaybeProcessUndecryptablePackets() {
-  process_undecryptable_packets_alarm_->Cancel();
+  process_undecryptable_packets_alarm().Cancel();
 
   if (undecryptable_packets_.empty() ||
       encryption_level_ == ENCRYPTION_INITIAL) {
@@ -4695,15 +4563,15 @@
 void QuicConnection::CancelAllAlarms() {
   QUIC_DVLOG(1) << "Cancelling all QuicConnection alarms.";
 
-  ack_alarm_->PermanentCancel();
+  ack_alarm().PermanentCancel();
   ping_manager_.Stop();
-  retransmission_alarm_->PermanentCancel();
-  send_alarm_->PermanentCancel();
-  mtu_discovery_alarm_->PermanentCancel();
-  process_undecryptable_packets_alarm_->PermanentCancel();
-  discard_previous_one_rtt_keys_alarm_->PermanentCancel();
-  discard_zero_rtt_decryption_keys_alarm_->PermanentCancel();
-  multi_port_probing_alarm_->PermanentCancel();
+  retransmission_alarm().PermanentCancel();
+  send_alarm().PermanentCancel();
+  mtu_discovery_alarm().PermanentCancel();
+  process_undecryptable_packets_alarm().PermanentCancel();
+  discard_previous_one_rtt_keys_alarm().PermanentCancel();
+  discard_zero_rtt_decryption_keys_alarm().PermanentCancel();
+  multi_port_probing_alarm().PermanentCancel();
   blackhole_detector_.StopDetection(/*permanent=*/true);
   idle_network_detector_.StopDetection();
 }
@@ -4748,10 +4616,10 @@
 
 void QuicConnection::SetRetransmissionAlarm() {
   if (!connected_) {
-    if (retransmission_alarm_->IsSet()) {
+    if (retransmission_alarm().IsSet()) {
       QUIC_BUG(quic_bug_10511_29)
           << ENDPOINT << "Retransmission alarm is set while disconnected";
-      retransmission_alarm_->Cancel();
+      retransmission_alarm().Cancel();
     }
     return;
   }
@@ -4762,7 +4630,7 @@
   if (LimitedByAmplificationFactor(packet_creator_.max_packet_length())) {
     // Do not set retransmission timer if connection is anti-amplification limit
     // throttled. Otherwise, nothing can be sent when timer fires.
-    retransmission_alarm_->Cancel();
+    retransmission_alarm().Cancel();
     return;
   }
   PacketNumberSpace packet_number_space;
@@ -4775,25 +4643,25 @@
     // is in flight.
     if (perspective_ == Perspective::IS_SERVER) {
       // No need to arm PTO on server side.
-      retransmission_alarm_->Cancel();
+      retransmission_alarm().Cancel();
       return;
     }
-    if (retransmission_alarm_->IsSet() &&
-        GetRetransmissionDeadline() > retransmission_alarm_->deadline()) {
+    if (retransmission_alarm().IsSet() &&
+        GetRetransmissionDeadline() > retransmission_alarm().deadline()) {
       // Do not postpone armed PTO on the client side.
       return;
     }
   }
 
-  retransmission_alarm_->Update(GetRetransmissionDeadline(), kAlarmGranularity);
+  retransmission_alarm().Update(GetRetransmissionDeadline(), kAlarmGranularity);
 }
 
 void QuicConnection::MaybeSetMtuAlarm(QuicPacketNumber sent_packet_number) {
-  if (mtu_discovery_alarm_->IsSet() ||
+  if (mtu_discovery_alarm().IsSet() ||
       !mtu_discoverer_.ShouldProbeMtu(sent_packet_number)) {
     return;
   }
-  mtu_discovery_alarm_->Set(clock_->ApproximateNow());
+  mtu_discovery_alarm().Set(clock_->ApproximateNow());
 }
 
 QuicConnection::ScopedPacketFlusher::ScopedPacketFlusher(
@@ -4825,23 +4693,23 @@
           !connection_->CanWrite(NO_RETRANSMITTABLE_DATA)) {
         // Cancel ACK alarm if connection is write blocked, and ACK will be
         // sent when connection gets unblocked.
-        connection_->ack_alarm_->Cancel();
-      } else if (!connection_->ack_alarm_->IsSet() ||
-                 connection_->ack_alarm_->deadline() > ack_timeout) {
-        connection_->ack_alarm_->Update(ack_timeout, QuicTime::Delta::Zero());
+        connection_->ack_alarm().Cancel();
+      } else if (!connection_->ack_alarm().IsSet() ||
+                 connection_->ack_alarm().deadline() > ack_timeout) {
+        connection_->ack_alarm().Update(ack_timeout, QuicTime::Delta::Zero());
       }
     }
-    if (connection_->ack_alarm_->IsSet() &&
-        connection_->ack_alarm_->deadline() <=
+    if (connection_->ack_alarm().IsSet() &&
+        connection_->ack_alarm().deadline() <=
             connection_->clock_->ApproximateNow()) {
       // An ACK needs to be sent right now. This ACK did not get bundled
       // because either there was no data to write or packets were marked as
       // received after frames were queued in the generator.
-      if (connection_->send_alarm_->IsSet() &&
-          connection_->send_alarm_->deadline() <=
+      if (connection_->send_alarm().IsSet() &&
+          connection_->send_alarm().deadline() <=
               connection_->clock_->ApproximateNow()) {
         // If send alarm will go off soon, let send alarm send the ACK.
-        connection_->ack_alarm_->Cancel();
+        connection_->ack_alarm().Cancel();
       } else if (connection_->SupportsMultiplePacketNumberSpaces()) {
         connection_->SendAllPendingAcks();
       } else {
@@ -5171,11 +5039,11 @@
 
 void QuicConnection::DisableMtuDiscovery() {
   mtu_discoverer_.Disable();
-  mtu_discovery_alarm_->Cancel();
+  mtu_discovery_alarm().Cancel();
 }
 
 void QuicConnection::DiscoverMtu() {
-  QUICHE_DCHECK(!mtu_discovery_alarm_->IsSet());
+  QUICHE_DCHECK(!mtu_discovery_alarm().IsSet());
 
   const QuicPacketNumber largest_sent_packet =
       sent_packet_manager_.GetLargestSentPacket();
@@ -5184,7 +5052,7 @@
     SendMtuDiscoveryPacket(
         mtu_discoverer_.GetUpdatedMtuProbeSize(largest_sent_packet));
   }
-  QUICHE_DCHECK(!mtu_discovery_alarm_->IsSet());
+  QUICHE_DCHECK(!mtu_discovery_alarm().IsSet());
 }
 
 void QuicConnection::OnEffectivePeerMigrationValidated(
@@ -5720,7 +5588,7 @@
 }
 
 void QuicConnection::ResetAckStates() {
-  ack_alarm_->Cancel();
+  ack_alarm().Cancel();
   uber_received_packet_manager_.ResetAckStates(encryption_level_);
 }
 
@@ -5829,7 +5697,7 @@
 void QuicConnection::SendAllPendingAcks() {
   QUICHE_DCHECK(SupportsMultiplePacketNumberSpaces());
   QUIC_DVLOG(1) << ENDPOINT << "Trying to send all pending ACKs";
-  ack_alarm_->Cancel();
+  ack_alarm().Cancel();
   QuicTime earliest_ack_timeout =
       uber_received_packet_manager_.GetEarliestAckTimeout();
   QUIC_BUG_IF(quic_bug_12714_32, !earliest_ack_timeout.IsInitialized());
@@ -5896,7 +5764,7 @@
       uber_received_packet_manager_.GetEarliestAckTimeout();
   if (timeout.IsInitialized()) {
     // If there are ACKs pending, re-arm ack alarm.
-    ack_alarm_->Update(timeout, kAlarmGranularity);
+    ack_alarm().Update(timeout, kAlarmGranularity);
   }
   // Only try to bundle retransmittable data with ACK frame if default
   // encryption level is forward secure.
@@ -6269,7 +6137,7 @@
     // The multi-port path should have just finished the recent probe and
     // waiting for the next one.
     context = std::move(multi_port_path_context_);
-    multi_port_probing_alarm_->Cancel();
+    multi_port_probing_alarm().Cancel();
     QUIC_CLIENT_HISTOGRAM_ENUM(
         "QuicConnection.MultiPortPathStatusWhenMigrating",
         MultiPortStatusOnMigration::kWaitingForRefreshValidation,
@@ -6362,7 +6230,7 @@
 }
 
 void QuicConnection::OnKeepAliveTimeout() {
-  if (retransmission_alarm_->IsSet() ||
+  if (retransmission_alarm().IsSet() ||
       !visitor_->ShouldKeepConnectionAlive()) {
     return;
   }
@@ -6370,7 +6238,7 @@
 }
 
 void QuicConnection::OnRetransmittableOnWireTimeout() {
-  if (retransmission_alarm_->IsSet() ||
+  if (retransmission_alarm().IsSet() ||
       !visitor_->ShouldKeepConnectionAlive()) {
     return;
   }
@@ -7154,7 +7022,7 @@
   QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective());
   alternative_path_.validated = true;
   multi_port_path_context_ = std::move(context);
-  multi_port_probing_alarm_->Set(clock_->ApproximateNow() +
+  multi_port_probing_alarm().Set(clock_->ApproximateNow() +
                                  multi_port_probing_interval_);
   if (multi_port_stats_ != nullptr) {
     multi_port_stats_->num_successful_probes++;
@@ -7177,7 +7045,7 @@
       alternative_path_.peer_address !=
           multi_port_path_context_->peer_address() ||
       !visitor_->ShouldKeepConnectionAlive() ||
-      multi_port_probing_alarm_->IsSet()) {
+      multi_port_probing_alarm().IsSet()) {
     return;
   }
   if (multi_port_stats_ != nullptr) {
@@ -7198,7 +7066,7 @@
   }
   auto multi_port_validation_result_delegate =
       std::make_unique<MultiPortPathValidationResultDelegate>(connection_);
-  connection_->multi_port_probing_alarm_->Cancel();
+  connection_->multi_port_probing_alarm().Cancel();
   connection_->multi_port_path_context_ = nullptr;
   connection_->multi_port_stats_->num_multi_port_paths_created++;
   connection_->ValidatePath(std::move(path_context),
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h
index 6a4ddfe..0565b1a 100644
--- a/quiche/quic/core/quic_connection.h
+++ b/quiche/quic/core/quic_connection.h
@@ -38,6 +38,7 @@
 #include "quiche/quic/core/quic_alarm.h"
 #include "quiche/quic/core/quic_alarm_factory.h"
 #include "quiche/quic/core/quic_blocked_writer_interface.h"
+#include "quiche/quic/core/quic_connection_alarms.h"
 #include "quiche/quic/core/quic_connection_context.h"
 #include "quiche/quic/core/quic_connection_id.h"
 #include "quiche/quic/core/quic_connection_id_manager.h"
@@ -2029,6 +2030,43 @@
 
   bool PeerAddressChanged() const;
 
+  QuicAlarm& ack_alarm() { return alarms_.ack_alarm(); }
+  const QuicAlarm& ack_alarm() const { return alarms_.ack_alarm(); }
+  QuicAlarm& retransmission_alarm() { return alarms_.retransmission_alarm(); }
+  const QuicAlarm& retransmission_alarm() const {
+    return alarms_.retransmission_alarm();
+  }
+  QuicAlarm& send_alarm() { return alarms_.send_alarm(); }
+  const QuicAlarm& send_alarm() const { return alarms_.send_alarm(); }
+  QuicAlarm& mtu_discovery_alarm() { return alarms_.mtu_discovery_alarm(); }
+  const QuicAlarm& mtu_discovery_alarm() const {
+    return alarms_.mtu_discovery_alarm();
+  }
+  QuicAlarm& process_undecryptable_packets_alarm() {
+    return alarms_.process_undecryptable_packets_alarm();
+  }
+  const QuicAlarm& process_undecryptable_packets_alarm() const {
+    return alarms_.process_undecryptable_packets_alarm();
+  }
+  QuicAlarm& discard_previous_one_rtt_keys_alarm() {
+    return alarms_.discard_previous_one_rtt_keys_alarm();
+  }
+  const QuicAlarm& discard_previous_one_rtt_keys_alarm() const {
+    return alarms_.discard_previous_one_rtt_keys_alarm();
+  }
+  QuicAlarm& discard_zero_rtt_decryption_keys_alarm() {
+    return alarms_.discard_zero_rtt_decryption_keys_alarm();
+  }
+  const QuicAlarm& discard_zero_rtt_decryption_keys_alarm() const {
+    return alarms_.discard_zero_rtt_decryption_keys_alarm();
+  }
+  QuicAlarm& multi_port_probing_alarm() {
+    return alarms_.multi_port_probing_alarm();
+  }
+  const QuicAlarm& multi_port_probing_alarm() const {
+    return alarms_.multi_port_probing_alarm();
+  }
+
   QuicConnectionContext context_;
 
   QuicFramer framer_;
@@ -2156,27 +2194,9 @@
   // Arena to store class implementations within the QuicConnection.
   QuicConnectionArena arena_;
 
-  // An alarm that fires when an ACK should be sent to the peer.
-  QuicArenaScopedPtr<QuicAlarm> ack_alarm_;
-  // An alarm that fires when a packet needs to be retransmitted.
-  QuicArenaScopedPtr<QuicAlarm> retransmission_alarm_;
-  // An alarm that is scheduled when the SentPacketManager requires a delay
-  // before sending packets and fires when the packet may be sent.
-  QuicArenaScopedPtr<QuicAlarm> send_alarm_;
-  // An alarm that fires when an MTU probe should be sent.
-  QuicArenaScopedPtr<QuicAlarm> mtu_discovery_alarm_;
-  // An alarm that fires to process undecryptable packets when new decyrption
-  // keys are available.
-  QuicArenaScopedPtr<QuicAlarm> process_undecryptable_packets_alarm_;
-  // An alarm that fires to discard keys for the previous key phase some time
-  // after a key update has completed.
-  QuicArenaScopedPtr<QuicAlarm> discard_previous_one_rtt_keys_alarm_;
-  // An alarm that fires to discard 0-RTT decryption keys some time after the
-  // first 1-RTT packet has been decrypted. Only used on server connections with
-  // TLS handshaker.
-  QuicArenaScopedPtr<QuicAlarm> discard_zero_rtt_decryption_keys_alarm_;
-  // An alarm that fires to keep probing the multi-port path.
-  QuicArenaScopedPtr<QuicAlarm> multi_port_probing_alarm_;
+  // Alarms used by the connection.
+  QuicConnectionAlarms alarms_;
+
   // Neither visitor is owned by this class.
   QuicConnectionVisitorInterface* visitor_;
   QuicConnectionDebugVisitor* debug_visitor_;
diff --git a/quiche/quic/core/quic_connection_alarms.cc b/quiche/quic/core/quic_connection_alarms.cc
new file mode 100644
index 0000000..fcd29d8
--- /dev/null
+++ b/quiche/quic/core/quic_connection_alarms.cc
@@ -0,0 +1,157 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quiche/quic/core/quic_connection_alarms.h"
+
+#include "quiche/quic/core/quic_alarm.h"
+#include "quiche/quic/core/quic_connection.h"
+#include "quiche/quic/core/quic_connection_context.h"
+#include "quiche/common/platform/api/quiche_logging.h"
+
+namespace quic {
+
+namespace {
+
+// Base class of all alarms owned by a QuicConnection.
+class QuicConnectionAlarmDelegate : public QuicAlarm::Delegate {
+ public:
+  explicit QuicConnectionAlarmDelegate(QuicConnection* connection)
+      : connection_(connection) {}
+  QuicConnectionAlarmDelegate(const QuicConnectionAlarmDelegate&) = delete;
+  QuicConnectionAlarmDelegate& operator=(const QuicConnectionAlarmDelegate&) =
+      delete;
+
+  QuicConnectionContext* GetConnectionContext() override {
+    return (connection_ == nullptr) ? nullptr : connection_->context();
+  }
+
+ protected:
+  QuicConnection* connection_;
+};
+
+// An alarm that is scheduled to send an ack if a timeout occurs.
+class AckAlarmDelegate : public QuicConnectionAlarmDelegate {
+ public:
+  using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
+
+  void OnAlarm() override {
+    QUICHE_DCHECK(connection_->ack_frame_updated());
+    QUICHE_DCHECK(connection_->connected());
+    QuicConnection::ScopedPacketFlusher flusher(connection_);
+    if (connection_->SupportsMultiplePacketNumberSpaces()) {
+      connection_->SendAllPendingAcks();
+    } else {
+      connection_->SendAck();
+    }
+  }
+};
+
+// This alarm will be scheduled any time a data-bearing packet is sent out.
+// When the alarm goes off, the connection checks to see if the oldest packets
+// have been acked, and retransmit them if they have not.
+class RetransmissionAlarmDelegate : public QuicConnectionAlarmDelegate {
+ public:
+  using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
+
+  void OnAlarm() override {
+    QUICHE_DCHECK(connection_->connected());
+    connection_->OnRetransmissionTimeout();
+  }
+};
+
+// An alarm that is scheduled when the SentPacketManager requires a delay
+// before sending packets and fires when the packet may be sent.
+class SendAlarmDelegate : public QuicConnectionAlarmDelegate {
+ public:
+  using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
+
+  void OnAlarm() override {
+    QUICHE_DCHECK(connection_->connected());
+    connection_->OnSendAlarm();
+  }
+};
+
+class MtuDiscoveryAlarmDelegate : public QuicConnectionAlarmDelegate {
+ public:
+  using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
+
+  void OnAlarm() override {
+    QUICHE_DCHECK(connection_->connected());
+    connection_->DiscoverMtu();
+  }
+};
+
+class ProcessUndecryptablePacketsAlarmDelegate
+    : public QuicConnectionAlarmDelegate {
+ public:
+  using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
+
+  void OnAlarm() override {
+    QUICHE_DCHECK(connection_->connected());
+    QuicConnection::ScopedPacketFlusher flusher(connection_);
+    connection_->MaybeProcessUndecryptablePackets();
+  }
+};
+
+class DiscardPreviousOneRttKeysAlarmDelegate
+    : public QuicConnectionAlarmDelegate {
+ public:
+  using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
+
+  void OnAlarm() override {
+    QUICHE_DCHECK(connection_->connected());
+    connection_->DiscardPreviousOneRttKeys();
+  }
+};
+
+class DiscardZeroRttDecryptionKeysAlarmDelegate
+    : public QuicConnectionAlarmDelegate {
+ public:
+  using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
+
+  void OnAlarm() override {
+    QUICHE_DCHECK(connection_->connected());
+    QUIC_DLOG(INFO) << "0-RTT discard alarm fired";
+    connection_->RemoveDecrypter(ENCRYPTION_ZERO_RTT);
+    connection_->RetireOriginalDestinationConnectionId();
+  }
+};
+
+class MultiPortProbingAlarmDelegate : public QuicConnectionAlarmDelegate {
+ public:
+  using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
+
+  void OnAlarm() override {
+    QUICHE_DCHECK(connection_->connected());
+    QUIC_DLOG(INFO) << "Alternative path probing alarm fired";
+    connection_->MaybeProbeMultiPortPath();
+  }
+};
+
+}  // namespace
+
+QuicConnectionAlarms::QuicConnectionAlarms(QuicConnection* connection,
+                                           QuicAlarmFactory& alarm_factory,
+                                           QuicConnectionArena& arena)
+    : ack_alarm_(alarm_factory.CreateAlarm(
+          arena.New<AckAlarmDelegate>(connection), &arena)),
+      retransmission_alarm_(alarm_factory.CreateAlarm(
+          arena.New<RetransmissionAlarmDelegate>(connection), &arena)),
+      send_alarm_(alarm_factory.CreateAlarm(
+          arena.New<SendAlarmDelegate>(connection), &arena)),
+      mtu_discovery_alarm_(alarm_factory.CreateAlarm(
+          arena.New<MtuDiscoveryAlarmDelegate>(connection), &arena)),
+      process_undecryptable_packets_alarm_(alarm_factory.CreateAlarm(
+          arena.New<ProcessUndecryptablePacketsAlarmDelegate>(connection),
+          &arena)),
+      discard_previous_one_rtt_keys_alarm_(alarm_factory.CreateAlarm(
+          arena.New<DiscardPreviousOneRttKeysAlarmDelegate>(connection),
+          &arena)),
+      discard_zero_rtt_decryption_keys_alarm_(alarm_factory.CreateAlarm(
+          arena.New<DiscardZeroRttDecryptionKeysAlarmDelegate>(connection),
+          &arena)),
+      multi_port_probing_alarm_(alarm_factory.CreateAlarm(
+          arena.New<MultiPortProbingAlarmDelegate>(connection), &arena)) {}
+
+}  // namespace quic
diff --git a/quiche/quic/core/quic_connection_alarms.h b/quiche/quic/core/quic_connection_alarms.h
new file mode 100644
index 0000000..7a3da27
--- /dev/null
+++ b/quiche/quic/core/quic_connection_alarms.h
@@ -0,0 +1,83 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_CONNECTION_ALARMS_H_
+#define QUICHE_QUIC_CORE_QUIC_CONNECTION_ALARMS_H_
+
+#include "quiche/quic/core/quic_alarm.h"
+#include "quiche/quic/core/quic_alarm_factory.h"
+#include "quiche/quic/core/quic_arena_scoped_ptr.h"
+#include "quiche/quic/core/quic_one_block_arena.h"
+
+namespace quic {
+
+class QuicConnection;
+
+class QUICHE_EXPORT QuicConnectionAlarms {
+ public:
+  QuicConnectionAlarms(QuicConnection* connection,
+                       QuicAlarmFactory& alarm_factory,
+                       QuicConnectionArena& arena);
+
+  QuicAlarm& ack_alarm() { return *ack_alarm_; }
+  QuicAlarm& retransmission_alarm() { return *retransmission_alarm_; }
+  QuicAlarm& send_alarm() { return *send_alarm_; }
+  QuicAlarm& mtu_discovery_alarm() { return *mtu_discovery_alarm_; }
+  QuicAlarm& process_undecryptable_packets_alarm() {
+    return *process_undecryptable_packets_alarm_;
+  }
+  QuicAlarm& discard_previous_one_rtt_keys_alarm() {
+    return *discard_previous_one_rtt_keys_alarm_;
+  }
+  QuicAlarm& discard_zero_rtt_decryption_keys_alarm() {
+    return *discard_zero_rtt_decryption_keys_alarm_;
+  }
+  QuicAlarm& multi_port_probing_alarm() { return *multi_port_probing_alarm_; }
+
+  const QuicAlarm& ack_alarm() const { return *ack_alarm_; }
+  const QuicAlarm& retransmission_alarm() const {
+    return *retransmission_alarm_;
+  }
+  const QuicAlarm& send_alarm() const { return *send_alarm_; }
+  const QuicAlarm& mtu_discovery_alarm() const { return *mtu_discovery_alarm_; }
+  const QuicAlarm& process_undecryptable_packets_alarm() const {
+    return *process_undecryptable_packets_alarm_;
+  }
+  const QuicAlarm& discard_previous_one_rtt_keys_alarm() const {
+    return *discard_previous_one_rtt_keys_alarm_;
+  }
+  const QuicAlarm& discard_zero_rtt_decryption_keys_alarm() const {
+    return *discard_zero_rtt_decryption_keys_alarm_;
+  }
+  const QuicAlarm& multi_port_probing_alarm() const {
+    return *multi_port_probing_alarm_;
+  }
+
+ private:
+  // An alarm that fires when an ACK should be sent to the peer.
+  QuicArenaScopedPtr<QuicAlarm> ack_alarm_;
+  // An alarm that fires when a packet needs to be retransmitted.
+  QuicArenaScopedPtr<QuicAlarm> retransmission_alarm_;
+  // An alarm that is scheduled when the SentPacketManager requires a delay
+  // before sending packets and fires when the packet may be sent.
+  QuicArenaScopedPtr<QuicAlarm> send_alarm_;
+  // An alarm that fires when an MTU probe should be sent.
+  QuicArenaScopedPtr<QuicAlarm> mtu_discovery_alarm_;
+  // An alarm that fires to process undecryptable packets when new decryption
+  // keys are available.
+  QuicArenaScopedPtr<QuicAlarm> process_undecryptable_packets_alarm_;
+  // An alarm that fires to discard keys for the previous key phase some time
+  // after a key update has completed.
+  QuicArenaScopedPtr<QuicAlarm> discard_previous_one_rtt_keys_alarm_;
+  // An alarm that fires to discard 0-RTT decryption keys some time after the
+  // first 1-RTT packet has been decrypted. Only used on server connections with
+  // TLS handshaker.
+  QuicArenaScopedPtr<QuicAlarm> discard_zero_rtt_decryption_keys_alarm_;
+  // An alarm that fires to keep probing the multi-port path.
+  QuicArenaScopedPtr<QuicAlarm> multi_port_probing_alarm_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_CORE_QUIC_CONNECTION_ALARMS_H_
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc
index 883b6a3..2f27ed8 100644
--- a/quiche/quic/core/quic_connection_test.cc
+++ b/quiche/quic/core/quic_connection_test.cc
@@ -424,52 +424,52 @@
 
   TestAlarmFactory::TestAlarm* GetAckAlarm() {
     return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
-        QuicConnectionPeer::GetAckAlarm(this));
+        &QuicConnectionPeer::GetAckAlarm(this));
   }
 
   TestAlarmFactory::TestAlarm* GetPingAlarm() {
     return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
-        QuicConnectionPeer::GetPingAlarm(this));
+        &QuicConnectionPeer::GetPingAlarm(this));
   }
 
   TestAlarmFactory::TestAlarm* GetRetransmissionAlarm() {
     return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
-        QuicConnectionPeer::GetRetransmissionAlarm(this));
+        &QuicConnectionPeer::GetRetransmissionAlarm(this));
   }
 
   TestAlarmFactory::TestAlarm* GetSendAlarm() {
     return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
-        QuicConnectionPeer::GetSendAlarm(this));
+        &QuicConnectionPeer::GetSendAlarm(this));
   }
 
   TestAlarmFactory::TestAlarm* GetTimeoutAlarm() {
     return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
-        QuicConnectionPeer::GetIdleNetworkDetectorAlarm(this));
+        &QuicConnectionPeer::GetIdleNetworkDetectorAlarm(this));
   }
 
   TestAlarmFactory::TestAlarm* GetMtuDiscoveryAlarm() {
     return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
-        QuicConnectionPeer::GetMtuDiscoveryAlarm(this));
+        &QuicConnectionPeer::GetMtuDiscoveryAlarm(this));
   }
 
   TestAlarmFactory::TestAlarm* GetProcessUndecryptablePacketsAlarm() {
     return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
-        QuicConnectionPeer::GetProcessUndecryptablePacketsAlarm(this));
+        &QuicConnectionPeer::GetProcessUndecryptablePacketsAlarm(this));
   }
 
   TestAlarmFactory::TestAlarm* GetDiscardPreviousOneRttKeysAlarm() {
     return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
-        QuicConnectionPeer::GetDiscardPreviousOneRttKeysAlarm(this));
+        &QuicConnectionPeer::GetDiscardPreviousOneRttKeysAlarm(this));
   }
 
   TestAlarmFactory::TestAlarm* GetDiscardZeroRttDecryptionKeysAlarm() {
     return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
-        QuicConnectionPeer::GetDiscardZeroRttDecryptionKeysAlarm(this));
+        &QuicConnectionPeer::GetDiscardZeroRttDecryptionKeysAlarm(this));
   }
 
   TestAlarmFactory::TestAlarm* GetBlackholeDetectorAlarm() {
     return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
-        QuicConnectionPeer::GetBlackholeDetectorAlarm(this));
+        &QuicConnectionPeer::GetBlackholeDetectorAlarm(this));
   }
 
   TestAlarmFactory::TestAlarm* GetRetirePeerIssuedConnectionIdAlarm() {
@@ -484,7 +484,7 @@
 
   TestAlarmFactory::TestAlarm* GetMultiPortProbingAlarm() {
     return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
-        QuicConnectionPeer::GetMultiPortProbingAlarm(this));
+        &QuicConnectionPeer::GetMultiPortProbingAlarm(this));
   }
 
   void PathDegradingTimeout() {
diff --git a/quiche/quic/test_tools/quic_connection_peer.cc b/quiche/quic/test_tools/quic_connection_peer.cc
index b08a332..7285922 100644
--- a/quiche/quic/test_tools/quic_connection_peer.cc
+++ b/quiche/quic/test_tools/quic_connection_peer.cc
@@ -117,48 +117,48 @@
 }
 
 // static
-QuicAlarm* QuicConnectionPeer::GetAckAlarm(QuicConnection* connection) {
-  return connection->ack_alarm_.get();
+QuicAlarm& QuicConnectionPeer::GetAckAlarm(QuicConnection* connection) {
+  return connection->alarms_.ack_alarm();
 }
 
 // static
-QuicAlarm* QuicConnectionPeer::GetPingAlarm(QuicConnection* connection) {
-  return connection->ping_manager_.alarm_.get();
+QuicAlarm& QuicConnectionPeer::GetPingAlarm(QuicConnection* connection) {
+  return *connection->ping_manager_.alarm_;
 }
 
 // static
-QuicAlarm* QuicConnectionPeer::GetRetransmissionAlarm(
+QuicAlarm& QuicConnectionPeer::GetRetransmissionAlarm(
     QuicConnection* connection) {
-  return connection->retransmission_alarm_.get();
+  return connection->alarms_.retransmission_alarm();
 }
 
 // static
-QuicAlarm* QuicConnectionPeer::GetSendAlarm(QuicConnection* connection) {
-  return connection->send_alarm_.get();
+QuicAlarm& QuicConnectionPeer::GetSendAlarm(QuicConnection* connection) {
+  return connection->alarms_.send_alarm();
 }
 
 // static
-QuicAlarm* QuicConnectionPeer::GetMtuDiscoveryAlarm(
+QuicAlarm& QuicConnectionPeer::GetMtuDiscoveryAlarm(
     QuicConnection* connection) {
-  return connection->mtu_discovery_alarm_.get();
+  return connection->alarms_.mtu_discovery_alarm();
 }
 
 // static
-QuicAlarm* QuicConnectionPeer::GetProcessUndecryptablePacketsAlarm(
+QuicAlarm& QuicConnectionPeer::GetProcessUndecryptablePacketsAlarm(
     QuicConnection* connection) {
-  return connection->process_undecryptable_packets_alarm_.get();
+  return connection->alarms_.process_undecryptable_packets_alarm();
 }
 
 // static
-QuicAlarm* QuicConnectionPeer::GetDiscardPreviousOneRttKeysAlarm(
+QuicAlarm& QuicConnectionPeer::GetDiscardPreviousOneRttKeysAlarm(
     QuicConnection* connection) {
-  return connection->discard_previous_one_rtt_keys_alarm_.get();
+  return connection->alarms_.discard_previous_one_rtt_keys_alarm();
 }
 
 // static
-QuicAlarm* QuicConnectionPeer::GetDiscardZeroRttDecryptionKeysAlarm(
+QuicAlarm& QuicConnectionPeer::GetDiscardZeroRttDecryptionKeysAlarm(
     QuicConnection* connection) {
-  return connection->discard_zero_rtt_decryption_keys_alarm_.get();
+  return connection->alarms_.discard_zero_rtt_decryption_keys_alarm();
 }
 
 // static
@@ -322,9 +322,9 @@
 }
 
 // static
-QuicAlarm* QuicConnectionPeer::GetBlackholeDetectorAlarm(
+QuicAlarm& QuicConnectionPeer::GetBlackholeDetectorAlarm(
     QuicConnection* connection) {
-  return connection->blackhole_detector_.alarm_.get();
+  return *connection->blackhole_detector_.alarm_;
 }
 
 // static
@@ -352,9 +352,9 @@
 }
 
 // static
-QuicAlarm* QuicConnectionPeer::GetIdleNetworkDetectorAlarm(
+QuicAlarm& QuicConnectionPeer::GetIdleNetworkDetectorAlarm(
     QuicConnection* connection) {
-  return connection->idle_network_detector_.alarm_.get();
+  return *connection->idle_network_detector_.alarm_;
 }
 
 // static
@@ -364,9 +364,9 @@
 }
 
 // static
-QuicAlarm* QuicConnectionPeer::GetMultiPortProbingAlarm(
+QuicAlarm& QuicConnectionPeer::GetMultiPortProbingAlarm(
     QuicConnection* connection) {
-  return connection->multi_port_probing_alarm_.get();
+  return connection->alarms_.multi_port_probing_alarm();
 }
 
 // static
diff --git a/quiche/quic/test_tools/quic_connection_peer.h b/quiche/quic/test_tools/quic_connection_peer.h
index 5017d7a..32db849 100644
--- a/quiche/quic/test_tools/quic_connection_peer.h
+++ b/quiche/quic/test_tools/quic_connection_peer.h
@@ -78,16 +78,16 @@
 
   static QuicFramer* GetFramer(QuicConnection* connection);
 
-  static QuicAlarm* GetAckAlarm(QuicConnection* connection);
-  static QuicAlarm* GetPingAlarm(QuicConnection* connection);
-  static QuicAlarm* GetRetransmissionAlarm(QuicConnection* connection);
-  static QuicAlarm* GetSendAlarm(QuicConnection* connection);
-  static QuicAlarm* GetMtuDiscoveryAlarm(QuicConnection* connection);
-  static QuicAlarm* GetProcessUndecryptablePacketsAlarm(
+  static QuicAlarm& GetAckAlarm(QuicConnection* connection);
+  static QuicAlarm& GetPingAlarm(QuicConnection* connection);
+  static QuicAlarm& GetRetransmissionAlarm(QuicConnection* connection);
+  static QuicAlarm& GetSendAlarm(QuicConnection* connection);
+  static QuicAlarm& GetMtuDiscoveryAlarm(QuicConnection* connection);
+  static QuicAlarm& GetProcessUndecryptablePacketsAlarm(
       QuicConnection* connection);
-  static QuicAlarm* GetDiscardPreviousOneRttKeysAlarm(
+  static QuicAlarm& GetDiscardPreviousOneRttKeysAlarm(
       QuicConnection* connection);
-  static QuicAlarm* GetDiscardZeroRttDecryptionKeysAlarm(
+  static QuicAlarm& GetDiscardZeroRttDecryptionKeysAlarm(
       QuicConnection* connection);
   static QuicAlarm* GetRetirePeerIssuedConnectionIdAlarm(
       QuicConnection* connection);
@@ -136,7 +136,7 @@
   static QuicNetworkBlackholeDetector& GetBlackholeDetector(
       QuicConnection* connection);
 
-  static QuicAlarm* GetBlackholeDetectorAlarm(QuicConnection* connection);
+  static QuicAlarm& GetBlackholeDetectorAlarm(QuicConnection* connection);
 
   static QuicTime GetPathDegradingDeadline(QuicConnection* connection);
 
@@ -145,7 +145,7 @@
   static QuicTime GetPathMtuReductionDetectionDeadline(
       QuicConnection* connection);
 
-  static QuicAlarm* GetIdleNetworkDetectorAlarm(QuicConnection* connection);
+  static QuicAlarm& GetIdleNetworkDetectorAlarm(QuicConnection* connection);
 
   static QuicTime GetIdleNetworkDeadline(QuicConnection* connection);
 
@@ -220,7 +220,7 @@
 
   static void FlushCoalescedPacket(QuicConnection* connection);
 
-  static QuicAlarm* GetMultiPortProbingAlarm(QuicConnection* connection);
+  static QuicAlarm& GetMultiPortProbingAlarm(QuicConnection* connection);
 
   static void SetInProbeTimeOut(QuicConnection* connection, bool value);