Add method to serialize connection close packet with large packet number

PiperOrigin-RevId: 656213063
diff --git a/quiche/quic/core/http/end_to_end_test.cc b/quiche/quic/core/http/end_to_end_test.cc
index 64ff5a9..6bdf826 100644
--- a/quiche/quic/core/http/end_to_end_test.cc
+++ b/quiche/quic/core/http/end_to_end_test.cc
@@ -7704,6 +7704,45 @@
   server_thread_->Resume();
 }
 
+TEST_P(EndToEndTest, SerializeConnectionClosePacketWithLargestPacketNumber) {
+  ASSERT_TRUE(Initialize());
+  if (!version_.UsesTls()) {
+    return;
+  }
+  EXPECT_TRUE(client_->client()->WaitForHandshakeConfirmed());
+
+  std::unique_ptr<SerializedPacket> connection_close_packet =
+      GetClientConnection()->SerializeLargePacketNumberConnectionClosePacket(
+          QUIC_CLIENT_LOST_NETWORK_ACCESS, "EndToEndTest");
+  ASSERT_NE(connection_close_packet, nullptr);
+
+  // Send 50 requests to increase the packet number.
+  for (int i = 0; i < 50; ++i) {
+    EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+  }
+
+  server_thread_->Pause();
+  QuicDispatcher* dispatcher =
+      QuicServerPeer::GetDispatcher(server_thread_->server());
+  EXPECT_EQ(dispatcher->NumSessions(), 1);
+  server_thread_->Resume();
+
+  // Send the connection close packet to the server.
+  QUIC_LOG(INFO) << "Sending close connection packet";
+  client_writer_->WritePacket(
+      connection_close_packet->encrypted_buffer,
+      connection_close_packet->encrypted_length,
+      client_->client()->network_helper()->GetLatestClientAddress().host(),
+      server_address_, nullptr, packet_writer_params_);
+
+  // Wait for the server to close the connection.
+  EXPECT_TRUE(
+      server_thread_->WaitUntil([&] { return dispatcher->NumSessions() == 0; },
+                                QuicTime::Delta::FromSeconds(5)));
+
+  EXPECT_EQ("", client_->SendSynchronousRequest("/foo"));
+  EXPECT_THAT(client_->connection_error(), IsError(QUIC_PUBLIC_RESET));
+}
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index c81536b..9e56dae 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -7342,6 +7342,18 @@
   blackhole_detector_.OnAlarm();
 }
 
+std::unique_ptr<SerializedPacket>
+QuicConnection::SerializeLargePacketNumberConnectionClosePacket(
+    QuicErrorCode error, const std::string& error_details) {
+  QUICHE_DCHECK(IsHandshakeConfirmed());
+  QUICHE_DCHECK(!error_details.empty());
+  if (!IsHandshakeConfirmed()) {
+    return nullptr;
+  }
+  return packet_creator_.SerializeLargePacketNumberConnectionClosePacket(
+      GetLargestAckedPacket(), error, error_details);
+}
+
 #undef ENDPOINT  // undef for jumbo builds
 
 }  // namespace quic
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h
index a879b6c..c34161b 100644
--- a/quiche/quic/core/quic_connection.h
+++ b/quiche/quic/core/quic_connection.h
@@ -1377,6 +1377,22 @@
   void OnNetworkBlackholeDetectorAlarm() override;
   void OnPingAlarm() override;
 
+  // Create a CONNECTION_CLOSE packet with a large packet number.
+  // If this method is called before handshake is confirmed, this method returns
+  // nullptr.
+  // This packet can be pre-generated and other processes can send it later to
+  // close the connection.
+  // For example, on Android, the system server stores this packet and sends it
+  // to the server when the app is frozen, loses network access due to the
+  // firewall, or crashes. This will stop servers sending packets to the
+  // device and wasting the device battery.
+  // Note that the generated packet uses the packet number larger than the
+  // current largest acked packet number by (1 << 31) + 1 but the server will
+  // ignore this packet after the connection uses this packet number.
+  std::unique_ptr<SerializedPacket>
+  SerializeLargePacketNumberConnectionClosePacket(
+      QuicErrorCode error, const std::string& error_details);
+
  protected:
   // Calls cancel() on all the alarms owned by this connection.
   void CancelAllAlarms();
diff --git a/quiche/quic/core/quic_error_codes.cc b/quiche/quic/core/quic_error_codes.cc
index 51e11e8..daf62b4 100644
--- a/quiche/quic/core/quic_error_codes.cc
+++ b/quiche/quic/core/quic_error_codes.cc
@@ -286,6 +286,7 @@
     RETURN_STRING_LITERAL(QUIC_TLS_KEYING_MATERIAL_EXPORT_NOT_AVAILABLE);
     RETURN_STRING_LITERAL(QUIC_UNEXPECTED_DATA_BEFORE_ENCRYPTION_ESTABLISHED);
     RETURN_STRING_LITERAL(QUIC_SERVER_UNHEALTHY);
+    RETURN_STRING_LITERAL(QUIC_CLIENT_LOST_NETWORK_ACCESS);
 
     RETURN_STRING_LITERAL(QUIC_LAST_ERROR);
     // Intentionally have no default case, so we'll break the build
@@ -799,6 +800,8 @@
       return {true, static_cast<uint64_t>(INTERNAL_ERROR)};
     case QUIC_HANDSHAKE_FAILED_PACKETS_BUFFERED_TOO_LONG:
       return {true, static_cast<uint64_t>(NO_IETF_QUIC_ERROR)};
+    case QUIC_CLIENT_LOST_NETWORK_ACCESS:
+      return {true, static_cast<uint64_t>(NO_IETF_QUIC_ERROR)};
     case QUIC_LAST_ERROR:
       return {false, static_cast<uint64_t>(QUIC_LAST_ERROR)};
   }
diff --git a/quiche/quic/core/quic_error_codes.h b/quiche/quic/core/quic_error_codes.h
index a1ea0d4..a28d595 100644
--- a/quiche/quic/core/quic_error_codes.h
+++ b/quiche/quic/core/quic_error_codes.h
@@ -622,8 +622,11 @@
   // Error code related to backend health-check.
   QUIC_SERVER_UNHEALTHY = 213,
 
+  // Client application lost network access.
+  QUIC_CLIENT_LOST_NETWORK_ACCESS = 215,
+
   // No error. Used as bound while iterating.
-  QUIC_LAST_ERROR = 215,
+  QUIC_LAST_ERROR = 216,
 };
 // QuicErrorCodes is encoded as four octets on-the-wire when doing Google QUIC,
 // or a varint62 when doing IETF QUIC. Ensure that its value does not exceed
diff --git a/quiche/quic/core/quic_packet_creator.cc b/quiche/quic/core/quic_packet_creator.cc
index ca35a07..8823a5c 100644
--- a/quiche/quic/core/quic_packet_creator.cc
+++ b/quiche/quic/core/quic_packet_creator.cc
@@ -1038,6 +1038,64 @@
   return serialize_packet;
 }
 
+std::unique_ptr<SerializedPacket>
+QuicPacketCreator::SerializeLargePacketNumberConnectionClosePacket(
+    QuicPacketNumber largest_acked_packet, QuicErrorCode error,
+    const std::string& error_details) {
+  QUICHE_DCHECK_EQ(packet_.encryption_level, ENCRYPTION_FORWARD_SECURE)
+      << ENDPOINT;
+  // Largest packet number is 2^62 - 1 but the packet number is encoded to 1 to
+  // 4 bytes.
+  // Receiver decodes packet number assuming the packet number is less than or
+  // equal to (largest packet number that has been successfully processed) + 1
+  // + (1 << (packet_number_length - 1)).
+  // So, generate a packet with the largest packet number in this range.
+  // Note that FillPacketHeader increments before fills the header.
+  const QuicPacketNumber largest_packet_number(
+      (largest_acked_packet.IsInitialized()
+           ? largest_acked_packet
+           : framer_->first_sending_packet_number()) +
+      (1L << 31));
+  ScopedPacketContextSwitcher switcher(largest_packet_number,
+                                       PACKET_4BYTE_PACKET_NUMBER,
+                                       ENCRYPTION_FORWARD_SECURE, &packet_);
+
+  QuicPacketHeader header;
+  FillPacketHeader(&header);
+
+  QUIC_DVLOG(2) << ENDPOINT << "Serializing connection close packet " << header;
+
+  QuicFrames frames;
+  QuicConnectionCloseFrame close_frame(transport_version(), error,
+                                       NO_IETF_QUIC_ERROR, error_details, 0);
+  frames.push_back(QuicFrame(&close_frame));
+
+  std::unique_ptr<char[]> buffer(new char[kMaxOutgoingPacketSize]);
+  const size_t length =
+      framer_->BuildDataPacket(header, frames, buffer.get(),
+                               max_plaintext_size_, packet_.encryption_level);
+  QUICHE_DCHECK(length) << ENDPOINT;
+
+  const size_t encrypted_length = framer_->EncryptInPlace(
+      packet_.encryption_level, packet_.packet_number,
+      GetStartOfEncryptedData(framer_->transport_version(), header), length,
+      kMaxOutgoingPacketSize, buffer.get());
+  QUICHE_DCHECK(encrypted_length) << ENDPOINT;
+
+  std::unique_ptr<SerializedPacket> serialize_packet(
+      new SerializedPacket(header.packet_number, header.packet_number_length,
+                           buffer.release(), encrypted_length,
+                           /*has_ack=*/false, /*has_stop_waiting=*/false));
+
+  serialize_packet->release_encrypted_buffer = [](const char* p) {
+    delete[] p;
+  };
+  serialize_packet->encryption_level = packet_.encryption_level;
+  serialize_packet->transmission_type = NOT_RETRANSMISSION;
+
+  return serialize_packet;
+}
+
 size_t QuicPacketCreator::BuildPaddedPathChallengePacket(
     const QuicPacketHeader& header, char* buffer, size_t packet_length,
     const QuicPathFrameBuffer& payload, EncryptionLevel level) {
diff --git a/quiche/quic/core/quic_packet_creator.h b/quiche/quic/core/quic_packet_creator.h
index 6c3fc73..87c2000 100644
--- a/quiche/quic/core/quic_packet_creator.h
+++ b/quiche/quic/core/quic_packet_creator.h
@@ -262,6 +262,21 @@
       const quiche::QuicheCircularDeque<QuicPathFrameBuffer>& payloads,
       const bool is_padded);
 
+  // Create a CONNECTION_CLOSE packet with a large packet number.
+  // This packet can be pre-generated and other processes can send it later to
+  // close the connection.
+  // For example, on Android, the system server stores this packet and sends it
+  // to the server when the app is frozen, loses network access due to the
+  // firewall, or crashes. This will stop servers sending packets to the
+  // device and wasting the device battery.
+  // Note that the generated packet uses the packet number larger than the
+  // current largest acked packet number by (1 << 31) + 1 but the server will
+  // ignore this packet after the connection uses this packet number.
+  std::unique_ptr<SerializedPacket>
+  SerializeLargePacketNumberConnectionClosePacket(
+      QuicPacketNumber largest_acked_packet, QuicErrorCode error,
+      const std::string& error_details);
+
   // Add PATH_RESPONSE to current packet, flush before or afterwards if needed.
   bool AddPathResponseFrame(const QuicPathFrameBuffer& data_buffer);
 
diff --git a/quiche/quic/core/quic_packet_creator_test.cc b/quiche/quic/core/quic_packet_creator_test.cc
index 3946a90..0575b88 100644
--- a/quiche/quic/core/quic_packet_creator_test.cc
+++ b/quiche/quic/core/quic_packet_creator_test.cc
@@ -1202,6 +1202,26 @@
       encrypted->encrypted_buffer, encrypted->encrypted_length));
 }
 
+TEST_P(QuicPacketCreatorTest, SerializeLargePacketNumberConnectionClosePacket) {
+  creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+  std::unique_ptr<SerializedPacket> encrypted(
+      creator_.SerializeLargePacketNumberConnectionClosePacket(
+          QuicPacketNumber(1), QUIC_CLIENT_LOST_NETWORK_ACCESS,
+          "QuicPacketCreatorTest"));
+
+  InSequence s;
+  EXPECT_CALL(framer_visitor_, OnPacket());
+  EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+  EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+  EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_, _));
+  EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+  EXPECT_CALL(framer_visitor_, OnConnectionCloseFrame(_));
+  EXPECT_CALL(framer_visitor_, OnPacketComplete());
+
+  server_framer_.ProcessPacket(QuicEncryptedPacket(
+      encrypted->encrypted_buffer, encrypted->encrypted_length));
+}
+
 TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthLeastAwaiting) {
   if (!GetParam().version.SendsVariableLengthPacketNumberInLongHeader()) {
     EXPECT_EQ(PACKET_4BYTE_PACKET_NUMBER,