Update Crashpad to 25e67e285c01b2b0c7d654dc2238176f0f1a56c3

4c85c466b00c ios: Fix test failure on M1 ARM64 machines
460dbdceaeb4 ios: Unblock all signals corresponding to Mach exceptions
             on crash
243dffb04573 ios: Stop prune and upload thread when app is inactive and
             may suspend
25e67e285c01 ios: Track last NSException in ObjcException preprocessor

Change-Id: If360f268af8180c3706c35ede6c4ddde9638843d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3546087
Auto-Submit: Justin Cohen <justincohen@chromium.org>
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Commit-Queue: Robert Sesek <rsesek@chromium.org>
Cr-Commit-Position: refs/heads/main@{#984839}
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index b0420172..1b9e7efd 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: unknown
-Revision: cd13ea34ebca8f993d4d60fa40dc7fde3a95bee3
+Revision: 25e67e285c01b2b0c7d654dc2238176f0f1a56c3
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_ios.cc b/third_party/crashpad/crashpad/client/crashpad_client_ios.cc
index e98a232..af9524ef 100644
--- a/third_party/crashpad/crashpad/client/crashpad_client_ios.cc
+++ b/third_party/crashpad/crashpad/client/crashpad_client_ios.cc
@@ -14,6 +14,7 @@
 
 #include "client/crashpad_client.h"
 
+#include <signal.h>
 #include <unistd.h>
 
 #include <ios>
@@ -24,6 +25,7 @@
 #include "base/mac/scoped_mach_port.h"
 #include "client/ios_handler/exception_processor.h"
 #include "client/ios_handler/in_process_handler.h"
+#include "util/ios/raw_logging.h"
 #include "util/mach/exc_server_variants.h"
 #include "util/mach/exception_ports.h"
 #include "util/mach/mach_extensions.h"
@@ -249,8 +251,7 @@
     // inherit the task exception ports, and this process isn’t prepared to
     // handle them
     if (task != mach_task_self()) {
-      LOG(WARNING) << "task 0x" << std::hex << task << " != 0x"
-                   << mach_task_self();
+      CRASHPAD_RAW_LOG("MachException task != mach_task_self()");
       return KERN_FAILURE;
     }
 
@@ -264,7 +265,31 @@
                         old_state_count);
 
     // Respond with KERN_FAILURE so the system will continue to handle this
-    // exception as a crash.
+    // exception. xnu will turn this Mach exception into a signal and take the
+    // default action to terminate the process. However, if sigprocmask is
+    // called before this Mach exception returns (such as by another thread
+    // calling abort, see: Libc-1506.40.4/stdlib/FreeBSD/abort.c), the Mach
+    // exception will be converted into a signal but delivery will be blocked.
+    // Since concurrent exceptions lead to the losing thread sleeping
+    // indefinitely, if the abort thread never returns, the thread that
+    // triggered this Mach exception will repeatedly trap and the process will
+    // never terminate. If the abort thread didn’t have a user-space signal
+    // handler that slept forever, the abort would terminate the process even if
+    // all other signals had been blocked. Instead, unblock all signals
+    // corresponding to all Mach exceptions Crashpad is registered for before
+    // returning KERN_FAILURE. There is still racy behavior possible with this
+    // call to sigprocmask, but the repeated calls to CatchMachException here
+    // will eventually lead to termination.
+    sigset_t unblock_set;
+    sigemptyset(&unblock_set);
+    sigaddset(&unblock_set, SIGILL);  // EXC_BAD_INSTRUCTION
+    sigaddset(&unblock_set, SIGTRAP);  // EXC_BREAKPOINT
+    sigaddset(&unblock_set, SIGFPE);  // EXC_ARITHMETIC
+    sigaddset(&unblock_set, SIGBUS);  // EXC_BAD_ACCESS
+    sigaddset(&unblock_set, SIGSEGV);  // EXC_BAD_ACCESS
+    if (sigprocmask(SIG_UNBLOCK, &unblock_set, nullptr) != 0) {
+      CRASHPAD_RAW_LOG("sigprocmask");
+    }
     return KERN_FAILURE;
   }
 
diff --git a/third_party/crashpad/crashpad/client/ios_handler/exception_processor.mm b/third_party/crashpad/crashpad/client/ios_handler/exception_processor.mm
index 3398132..7b689c54 100644
--- a/third_party/crashpad/crashpad/client/ios_handler/exception_processor.mm
+++ b/third_party/crashpad/crashpad/client/ios_handler/exception_processor.mm
@@ -53,6 +53,8 @@
 #include "client/annotation.h"
 #include "client/simulate_crash_ios.h"
 
+namespace crashpad {
+
 namespace {
 
 // From 10.15.0 objc4-779.1/runtime/objc-exception.mm.
@@ -132,16 +134,67 @@
   return FormatStackTrace(addresses, 1024);
 }
 
-crashpad::ObjcExceptionDelegate* g_exception_delegate;
-objc_exception_preprocessor g_next_preprocessor;
-NSUncaughtExceptionHandler* g_next_uncaught_exception_handler;
+//! \brief Helper class to own the complex types used by the Objective-C
+//!     exception preprocessor.
+class ExceptionPreprocessorState {
+ public:
+  ExceptionPreprocessorState(const ExceptionPreprocessorState&) = delete;
+  ExceptionPreprocessorState& operator=(const ExceptionPreprocessorState&) =
+      delete;
+
+  static ExceptionPreprocessorState* Get() {
+    static ExceptionPreprocessorState* instance = []() {
+      return new ExceptionPreprocessorState();
+    }();
+    return instance;
+  }
+
+  // Inform the delegate of the uncaught exception and remove the global
+  // uncaught exception handler so we don't record this twice.
+  void HandleUncaughtException(NativeCPUContext* cpu_context) {
+    exception_delegate_->HandleUncaughtNSExceptionWithContext(cpu_context);
+
+    NSSetUncaughtExceptionHandler(next_uncaught_exception_handler_);
+    next_uncaught_exception_handler_ = nullptr;
+  }
+
+  id MaybeCallNextPreprocessor(id exception) {
+    return next_preprocessor_ ? next_preprocessor_(exception) : exception;
+  }
+
+  // Register the objc_setExceptionPreprocessor and NSUncaughtExceptionHandler.
+  void Install(ObjcExceptionDelegate* delegate);
+
+  // Restore the objc_setExceptionPreprocessor and NSUncaughtExceptionHandler.
+  void Uninstall();
+
+  NSException* last_exception() { return last_exception_; }
+  void set_last_exception(NSException* exception) {
+    [last_exception_ release];
+    last_exception_ = [exception retain];
+  }
+
+  ObjcExceptionDelegate* exception_delegate() { return exception_delegate_; }
+
+ private:
+  ExceptionPreprocessorState() = default;
+  ~ExceptionPreprocessorState() = default;
+
+  // Recorded last NSException in case the exception is caught and thrown again
+  // (without using objc_exception_rethrow.)
+  NSException* last_exception_ = nil;
+
+  ObjcExceptionDelegate* exception_delegate_ = nullptr;
+  objc_exception_preprocessor next_preprocessor_ = nullptr;
+  NSUncaughtExceptionHandler* next_uncaught_exception_handler_ = nullptr;
+};
 
 static void SetNSExceptionAnnotations(NSException* exception,
                                       std::string& name,
                                       std::string& reason) {
   @try {
     name = base::SysNSStringToUTF8(exception.name);
-    static crashpad::StringAnnotation<256> nameKey("exceptionName");
+    static StringAnnotation<256> nameKey("exceptionName");
     nameKey.Set(name);
   } @catch (id name_exception) {
     LOG(ERROR) << "Unable to read uncaught Objective-C exception name.";
@@ -149,7 +202,7 @@
 
   @try {
     reason = base::SysNSStringToUTF8(exception.reason);
-    static crashpad::StringAnnotation<512> reasonKey("exceptionReason");
+    static StringAnnotation<512> reasonKey("exceptionReason");
     reasonKey.Set(reason);
   } @catch (id reason_exception) {
     LOG(ERROR) << "Unable to read uncaught Objective-C exception reason.";
@@ -157,7 +210,7 @@
 
   @try {
     if (exception.userInfo) {
-      static crashpad::StringAnnotation<512> userInfoKey("exceptionUserInfo");
+      static StringAnnotation<512> userInfoKey("exceptionUserInfo");
       userInfoKey.Set(base::SysNSStringToUTF8(
           [NSString stringWithFormat:@"%@", exception.userInfo]));
     }
@@ -170,21 +223,24 @@
   std::string name, reason;
   SetNSExceptionAnnotations(exception, name, reason);
   NSArray<NSNumber*>* addressArray = [exception callStackReturnAddresses];
+
+  ObjcExceptionDelegate* exception_delegate =
+      ExceptionPreprocessorState::Get()->exception_delegate();
   if ([addressArray count] > 0) {
-    static crashpad::StringAnnotation<256> nameKey("UncaughtNSException");
+    static StringAnnotation<256> nameKey("UncaughtNSException");
     nameKey.Set("true");
     std::vector<uint64_t> addresses;
     for (NSNumber* address in addressArray)
       addresses.push_back([address unsignedLongLongValue]);
-    g_exception_delegate->HandleUncaughtNSException(&addresses[0],
-                                                    addresses.size());
+    exception_delegate->HandleUncaughtNSException(&addresses[0],
+                                                  addresses.size());
   } else {
     LOG(WARNING) << "Uncaught Objective-C exception name: " << name
                  << " reason: " << reason << " with no "
                  << " -callStackReturnAddresses.";
-    crashpad::NativeCPUContext cpu_context;
-    crashpad::CaptureContext(&cpu_context);
-    g_exception_delegate->HandleUncaughtNSExceptionWithContext(&cpu_context);
+    NativeCPUContext cpu_context;
+    CaptureContext(&cpu_context);
+    exception_delegate->HandleUncaughtNSExceptionWithContext(&cpu_context);
   }
 }
 
@@ -197,15 +253,13 @@
   SetNSExceptionAnnotations(exception, name, reason);
   LOG(WARNING) << "Handling Objective-C exception name: " << name
                << " reason: " << reason << " with sinkhole: " << sinkhole;
-  crashpad::NativeCPUContext cpu_context;
-  crashpad::CaptureContext(&cpu_context);
-  g_exception_delegate->HandleUncaughtNSExceptionWithContext(&cpu_context);
+  NativeCPUContext cpu_context;
+  CaptureContext(&cpu_context);
 
-  // Remove the uncaught exception handler so we don't record this twice.
-  NSSetUncaughtExceptionHandler(g_next_uncaught_exception_handler);
-  g_next_uncaught_exception_handler = nullptr;
-
-  return g_next_preprocessor ? g_next_preprocessor(exception) : exception;
+  ExceptionPreprocessorState* preprocessor_state =
+      ExceptionPreprocessorState::Get();
+  preprocessor_state->HandleUncaughtException(&cpu_context);
+  return preprocessor_state->MaybeCallNextPreprocessor(exception);
 }
 
 // Returns true if |path| equals |sinkhole| on device. Simulator paths prepend
@@ -226,13 +280,23 @@
 }
 
 id ObjcExceptionPreprocessor(id exception) {
+  // Some sinkholes don't use objc_exception_rethrow when they should, which
+  // would otherwise prevent the exception_preprocessor from getting called
+  // again. Because of this, track the most recently seen exception and
+  // ignore it.
+  ExceptionPreprocessorState* preprocessor_state =
+      ExceptionPreprocessorState::Get();
+  if ([preprocessor_state->last_exception() isEqual:exception]) {
+    return preprocessor_state->MaybeCallNextPreprocessor(exception);
+  }
+  preprocessor_state->set_last_exception(exception);
+
   static bool seen_first_exception;
 
-  static crashpad::StringAnnotation<256> firstexception("firstexception");
-  static crashpad::StringAnnotation<256> lastexception("lastexception");
-  static crashpad::StringAnnotation<1024> firstexception_bt(
-      "firstexception_bt");
-  static crashpad::StringAnnotation<1024> lastexception_bt("lastexception_bt");
+  static StringAnnotation<256> firstexception("firstexception");
+  static StringAnnotation<256> lastexception("lastexception");
+  static StringAnnotation<1024> firstexception_bt("firstexception_bt");
+  static StringAnnotation<1024> lastexception_bt("lastexception_bt");
   auto* key = seen_first_exception ? &lastexception : &firstexception;
   auto* bt_key = seen_first_exception ? &lastexception_bt : &firstexception_bt;
   NSString* value = [NSString
@@ -470,36 +534,41 @@
   }
 
   // Forward to the next preprocessor.
-  return g_next_preprocessor ? g_next_preprocessor(exception) : exception;
+  return preprocessor_state->MaybeCallNextPreprocessor(exception);
+}
+
+void ExceptionPreprocessorState::Install(ObjcExceptionDelegate* delegate) {
+  DCHECK(!next_preprocessor_);
+  exception_delegate_ = delegate;
+
+  // Preprocessor.
+  next_preprocessor_ =
+      objc_setExceptionPreprocessor(&ObjcExceptionPreprocessor);
+
+  // Uncaught processor.
+  next_uncaught_exception_handler_ = NSGetUncaughtExceptionHandler();
+  NSSetUncaughtExceptionHandler(&ObjcUncaughtExceptionHandler);
+}
+
+void ExceptionPreprocessorState::Uninstall() {
+  DCHECK(next_preprocessor_);
+  objc_setExceptionPreprocessor(next_preprocessor_);
+  next_preprocessor_ = nullptr;
+
+  NSSetUncaughtExceptionHandler(next_uncaught_exception_handler_);
+  next_uncaught_exception_handler_ = nullptr;
+
+  exception_delegate_ = nullptr;
 }
 
 }  // namespace
 
-namespace crashpad {
-
 void InstallObjcExceptionPreprocessor(ObjcExceptionDelegate* delegate) {
-  DCHECK(!g_next_preprocessor);
-
-  // Preprocessor.
-  g_next_preprocessor =
-      objc_setExceptionPreprocessor(&ObjcExceptionPreprocessor);
-
-  // Uncaught processor.
-  g_exception_delegate = delegate;
-  g_next_uncaught_exception_handler = NSGetUncaughtExceptionHandler();
-  NSSetUncaughtExceptionHandler(&ObjcUncaughtExceptionHandler);
+  ExceptionPreprocessorState::Get()->Install(delegate);
 }
 
 void UninstallObjcExceptionPreprocessor() {
-  DCHECK(g_next_preprocessor);
-
-  objc_setExceptionPreprocessor(g_next_preprocessor);
-  g_exception_delegate = nullptr;
-
-  NSSetUncaughtExceptionHandler(g_next_uncaught_exception_handler);
-  g_next_uncaught_exception_handler = nullptr;
-
-  g_next_preprocessor = nullptr;
+  ExceptionPreprocessorState::Get()->Uninstall();
 }
 
 }  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/client/ios_handler/in_process_handler.cc b/third_party/crashpad/crashpad/client/ios_handler/in_process_handler.cc
index ffcbceb..0642884 100644
--- a/third_party/crashpad/crashpad/client/ios_handler/in_process_handler.cc
+++ b/third_party/crashpad/crashpad/client/ios_handler/in_process_handler.cc
@@ -62,10 +62,7 @@
 InProcessHandler::InProcessHandler() = default;
 
 InProcessHandler::~InProcessHandler() {
-  if (upload_thread_started_ && upload_thread_) {
-    upload_thread_->Stop();
-  }
-  prune_thread_->Stop();
+  UpdatePruneAndUploadThreads(false);
 }
 
 bool InProcessHandler::Initialize(
@@ -103,13 +100,20 @@
   if (!CreateDirectory(base_dir_))
     return false;
 
+  bool is_app_extension = system_data_.IsExtension();
   prune_thread_.reset(new PruneIntermediateDumpsAndCrashReportsThread(
       database_.get(),
       PruneCondition::GetDefault(),
       base_dir_,
       bundle_identifier_and_seperator_,
-      system_data_.IsExtension()));
-  prune_thread_->Start();
+      is_app_extension));
+  if (is_app_extension || system_data_.IsApplicationActive())
+    prune_thread_->Start();
+
+  if (!is_app_extension) {
+    system_data_.SetActiveApplicationCallback(
+        [this](bool active) { UpdatePruneAndUploadThreads(active); });
+  }
 
   base::FilePath cached_writer_path = NewLockedFilePath();
   cached_writer_ = CreateWriterWithPath(cached_writer_path);
@@ -284,9 +288,29 @@
 }
 
 void InProcessHandler::StartProcessingPendingReports() {
-  if (!upload_thread_started_ && upload_thread_) {
-    upload_thread_->Start();
-    upload_thread_started_ = true;
+  if (!upload_thread_)
+    return;
+
+  upload_thread_enabled_ = true;
+  UpdatePruneAndUploadThreads(true);
+}
+
+void InProcessHandler::UpdatePruneAndUploadThreads(bool active) {
+  base::AutoLock lock_owner(prune_and_upload_lock_);
+  // TODO(crbug.com/crashpad/400): Consider moving prune and upload thread to
+  // BackgroundTasks and/or NSURLSession. This might allow uploads to continue
+  // in the background.
+  if (active) {
+    if (!prune_thread_->is_running())
+      prune_thread_->Start();
+    if (upload_thread_enabled_ && !upload_thread_->is_running()) {
+      upload_thread_->Start();
+    }
+  } else {
+    if (prune_thread_->is_running())
+      prune_thread_->Stop();
+    if (upload_thread_enabled_ && upload_thread_->is_running())
+      upload_thread_->Stop();
   }
 }
 
diff --git a/third_party/crashpad/crashpad/client/ios_handler/in_process_handler.h b/third_party/crashpad/crashpad/client/ios_handler/in_process_handler.h
index 096e477..f551b90 100644
--- a/third_party/crashpad/crashpad/client/ios_handler/in_process_handler.h
+++ b/third_party/crashpad/crashpad/client/ios_handler/in_process_handler.h
@@ -21,6 +21,7 @@
 #include <vector>
 
 #include "base/files/file_path.h"
+#include "base/synchronization/lock.h"
 #include "client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h"
 #include "handler/crash_report_upload_thread.h"
 #include "snapshot/ios/process_snapshot_ios_intermediate_dump.h"
@@ -202,6 +203,9 @@
     IOSIntermediateDumpWriter* writer_;
   };
 
+  //! \brief Manage the prune and upload thread when the active state changes.
+  void UpdatePruneAndUploadThreads(bool active);
+
   //! \brief Writes a minidump to the Crashpad database from the
   //!     \a process_snapshot, and triggers the upload_thread_ if started.
   void SaveSnapshot(ProcessSnapshotIOSIntermediateDump& process_snapshot);
@@ -230,7 +234,9 @@
   // in DumpExceptionFromMachException after aquiring the cached_writer_.
   void (*mach_exception_callback_for_testing_)() = nullptr;
 
-  bool upload_thread_started_ = false;
+  // Used to synchronize access to UpdatePruneAndUploadThreads().
+  base::Lock prune_and_upload_lock_;
+  std::atomic_bool upload_thread_enabled_ = false;
   std::map<std::string, std::string> annotations_;
   base::FilePath base_dir_;
   std::string cached_writer_path_;
diff --git a/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc b/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc
index 54af2c7..e61a604 100644
--- a/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc
+++ b/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc
@@ -104,12 +104,18 @@
   EXPECT_STREQ(system->CPUVendor().c_str(), "GenuineIntel");
 #elif defined(ARCH_CPU_ARM64)
   EXPECT_EQ(system->GetCPUArchitecture(), kCPUArchitectureARM64);
-  utsname uts;
-  ASSERT_EQ(uname(&uts), 0);
-  EXPECT_STREQ(system->MachineDescription().c_str(), uts.machine);
 #else
 #error Port to your CPU architecture
 #endif
+#if TARGET_OS_SIMULATOR
+  EXPECT_EQ(system->MachineDescription().substr(0, 13),
+            std::string("iOS Simulator"));
+#elif TARGET_OS_IPHONE
+  utsname uts;
+  ASSERT_EQ(uname(&uts), 0);
+  EXPECT_STREQ(system->MachineDescription().c_str(), uts.machine);
+#endif
+
   EXPECT_EQ(system->GetOperatingSystem(), SystemSnapshot::kOperatingSystemIOS);
 }
 
diff --git a/third_party/crashpad/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc b/third_party/crashpad/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc
index 1b7977cf..651b3907 100644
--- a/third_party/crashpad/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc
+++ b/third_party/crashpad/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc
@@ -28,6 +28,9 @@
 // The file extension used to indicate a file is locked.
 constexpr char kLockedExtension[] = ".locked";
 
+// Prune onces a day.
+constexpr time_t prune_interval = 60 * 60 * 24;
+
 // If the client finds a locked file matching it's own bundle id, unlock it
 // after 24 hours.
 constexpr time_t matching_bundle_locked_ttl = 60 * 60 * 24;
@@ -96,11 +99,12 @@
         base::FilePath pending_path,
         std::string bundle_identifier_and_seperator,
         bool is_extension)
-    : thread_(60 * 60 * 24, this),
+    : thread_(prune_interval, this),
       condition_(std::move(condition)),
       pending_path_(pending_path),
       bundle_identifier_and_seperator_(bundle_identifier_and_seperator),
       initial_work_delay_(is_extension ? extension_delay : app_delay),
+      last_start_time_(0),
       database_(database) {}
 
 PruneIntermediateDumpsAndCrashReportsThread::
@@ -116,9 +120,25 @@
 
 void PruneIntermediateDumpsAndCrashReportsThread::DoWork(
     const WorkerThread* thread) {
+  // This thread may be stopped and started a number of times throughout the
+  // lifetime of the process to prevent 0xdead10cc kills (see
+  // crbug.com/crashpad/400), but it should only run once per prune_interval
+  // after initial_work_delay_.
+  time_t now = time(nullptr);
+  if (now - last_start_time_ < prune_interval)
+    return;
+  last_start_time_ = now;
+
   internal::ScopedBackgroundTask scoper("PruneThread");
   database_->CleanDatabase(60 * 60 * 24 * 3);
+
+  // Here and below, respect Stop() being called after each task.
+  if (!thread_.is_running())
+    return;
   PruneCrashReportDatabase(database_, condition_.get());
+
+  if (!thread_.is_running())
+    return;
   if (!clean_old_intermediate_dumps_) {
     clean_old_intermediate_dumps_ = true;
     UnlockOldIntermediateDumps(pending_path_, bundle_identifier_and_seperator_);
diff --git a/third_party/crashpad/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h b/third_party/crashpad/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h
index b276423..cc40980 100644
--- a/third_party/crashpad/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h
+++ b/third_party/crashpad/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h
@@ -84,6 +84,9 @@
   //! It is expected to only be called from the same thread that called Start().
   void Stop() override;
 
+  //! \return `true` if the thread is running, `false` if it is not.
+  bool is_running() const { return thread_.is_running(); }
+
  private:
   // WorkerThread::Delegate:
   void DoWork(const WorkerThread* thread) override;
@@ -94,6 +97,7 @@
   std::string bundle_identifier_and_seperator_;
   bool clean_old_intermediate_dumps_;
   double initial_work_delay_;
+  time_t last_start_time_;
   CrashReportDatabase* database_;  // weak
 };
 
diff --git a/third_party/crashpad/crashpad/handler/crash_report_upload_thread.h b/third_party/crashpad/crashpad/handler/crash_report_upload_thread.h
index 70f1628..2af958d 100644
--- a/third_party/crashpad/crashpad/handler/crash_report_upload_thread.h
+++ b/third_party/crashpad/crashpad/handler/crash_report_upload_thread.h
@@ -108,6 +108,9 @@
   //! It is expected to only be called from the same thread that called Start().
   void Stop() override;
 
+  //! \return `true` if the thread is running, `false` if it is not.
+  bool is_running() const { return thread_.is_running(); }
+
  private:
   //! \brief The result code from UploadReport().
   enum class UploadResult {
diff --git a/third_party/crashpad/crashpad/util/ios/ios_system_data_collector.h b/third_party/crashpad/crashpad/util/ios/ios_system_data_collector.h
index 035f9d8..916a674 100644
--- a/third_party/crashpad/crashpad/util/ios/ios_system_data_collector.h
+++ b/third_party/crashpad/crashpad/util/ios/ios_system_data_collector.h
@@ -17,6 +17,7 @@
 
 #import <CoreFoundation/CoreFoundation.h>
 
+#include <functional>
 #include <string>
 
 namespace crashpad {
@@ -41,28 +42,25 @@
   int DaylightOffsetSeconds() const { return daylight_offset_seconds_; }
   const std::string& StandardName() const { return standard_name_; }
   const std::string& DaylightName() const { return daylight_name_; }
+  bool IsApplicationActive() const { return active_; }
 
   // Currently unused by minidump.
   int Orientation() const { return orientation_; }
 
- private:
-  // Notification handlers.
-  void InstallHandlers();
-  static void SystemTimeZoneDidChangeNotificationHandler(
-      CFNotificationCenterRef center,
-      void* observer,
-      CFStringRef name,
-      const void* object,
-      CFDictionaryRef userInfo);
-  void SystemTimeZoneDidChangeNotification();
+  // A completion callback that takes a bool indicating that the application has
+  // become active or inactive.
+  using ActiveApplicationCallback = std::function<void(bool)>;
 
-  static void OrientationDidChangeNotificationHandler(
-      CFNotificationCenterRef center,
-      void* observer,
-      CFStringRef name,
-      const void* object,
-      CFDictionaryRef userInfo);
+  void SetActiveApplicationCallback(ActiveApplicationCallback callback) {
+    active_application_callback_ = callback;
+  }
+
+ private:
+  // Notification handlers for time zone, orientation and active state.
+  void InstallHandlers();
+  void SystemTimeZoneDidChangeNotification();
   void OrientationDidChangeNotification();
+  void ApplicationDidChangeActiveNotification();
 
   int major_version_;
   int minor_version_;
@@ -72,6 +70,7 @@
   bool is_extension_;
   std::string machine_description_;
   int orientation_;
+  bool active_;
   int processor_count_;
   std::string cpu_vendor_;
   bool has_next_daylight_saving_time_;
@@ -80,6 +79,7 @@
   int daylight_offset_seconds_;
   std::string standard_name_;
   std::string daylight_name_;
+  ActiveApplicationCallback active_application_callback_;
 };
 
 }  // namespace internal
diff --git a/third_party/crashpad/crashpad/util/ios/ios_system_data_collector.mm b/third_party/crashpad/crashpad/util/ios/ios_system_data_collector.mm
index 366be46..6d39b6d2 100644
--- a/third_party/crashpad/crashpad/util/ios/ios_system_data_collector.mm
+++ b/third_party/crashpad/crashpad/util/ios/ios_system_data_collector.mm
@@ -49,6 +49,24 @@
   return value;
 }
 
+template <typename T, void (T::*M)(void)>
+void AddObserver(CFStringRef notification_name, T* observer) {
+  CFNotificationCenterAddObserver(
+      CFNotificationCenterGetLocalCenter(),
+      observer,
+      [](CFNotificationCenterRef center,
+         void* observer_vp,
+         CFNotificationName name,
+         const void* object,
+         CFDictionaryRef userInfo) {
+        T* observer = reinterpret_cast<T*>(observer_vp);
+        (observer->*M)();
+      },
+      notification_name,
+      nullptr,
+      CFNotificationSuspensionBehaviorDeliverImmediately);
+}
+
 }  // namespace
 
 namespace crashpad {
@@ -131,35 +149,30 @@
 
 void IOSSystemDataCollector::InstallHandlers() {
   // Timezone.
-  CFNotificationCenterAddObserver(
-      CFNotificationCenterGetLocalCenter(),
-      this,
-      IOSSystemDataCollector::SystemTimeZoneDidChangeNotificationHandler,
-      reinterpret_cast<CFStringRef>(NSSystemTimeZoneDidChangeNotification),
-      nullptr,
-      CFNotificationSuspensionBehaviorDeliverImmediately);
+  AddObserver<IOSSystemDataCollector,
+              &IOSSystemDataCollector::SystemTimeZoneDidChangeNotification>(
+      (__bridge CFStringRef)NSSystemTimeZoneDidChangeNotification, this);
   SystemTimeZoneDidChangeNotification();
 
   // Orientation.
-  CFNotificationCenterAddObserver(
-      CFNotificationCenterGetLocalCenter(),
-      this,
-      IOSSystemDataCollector::OrientationDidChangeNotificationHandler,
-      reinterpret_cast<CFStringRef>(UIDeviceOrientationDidChangeNotification),
-      nullptr,
-      CFNotificationSuspensionBehaviorDeliverImmediately);
+  AddObserver<IOSSystemDataCollector,
+              &IOSSystemDataCollector::OrientationDidChangeNotification>(
+      (__bridge CFStringRef)UIDeviceOrientationDidChangeNotification, this);
   OrientationDidChangeNotification();
-}
 
-// static
-void IOSSystemDataCollector::SystemTimeZoneDidChangeNotificationHandler(
-    CFNotificationCenterRef center,
-    void* observer,
-    CFStringRef name,
-    const void* object,
-    CFDictionaryRef userInfo) {
-  static_cast<IOSSystemDataCollector*>(observer)
-      ->SystemTimeZoneDidChangeNotification();
+  // Foreground/Background. Extensions shouldn't use UIApplication*.
+  if (!is_extension_) {
+    AddObserver<
+        IOSSystemDataCollector,
+        &IOSSystemDataCollector::ApplicationDidChangeActiveNotification>(
+        (__bridge CFStringRef)UIApplicationDidBecomeActiveNotification, this);
+    AddObserver<
+        IOSSystemDataCollector,
+        &IOSSystemDataCollector::ApplicationDidChangeActiveNotification>(
+        (__bridge CFStringRef)UIApplicationDidEnterBackgroundNotification,
+        this);
+    ApplicationDidChangeActiveNotification();
+  }
 }
 
 void IOSSystemDataCollector::SystemTimeZoneDidChangeNotification() {
@@ -197,21 +210,20 @@
   }
 }
 
-// static
-void IOSSystemDataCollector::OrientationDidChangeNotificationHandler(
-    CFNotificationCenterRef center,
-    void* observer,
-    CFStringRef name,
-    const void* object,
-    CFDictionaryRef userInfo) {
-  static_cast<IOSSystemDataCollector*>(observer)
-      ->OrientationDidChangeNotification();
-}
-
 void IOSSystemDataCollector::OrientationDidChangeNotification() {
   orientation_ =
       base::saturated_cast<int>([[UIDevice currentDevice] orientation]);
 }
 
+void IOSSystemDataCollector::ApplicationDidChangeActiveNotification() {
+  dispatch_assert_queue_debug(dispatch_get_main_queue());
+  bool old_active = active_;
+  active_ = [UIApplication sharedApplication].applicationState ==
+            UIApplicationStateActive;
+  if (active_ != old_active && active_application_callback_) {
+    active_application_callback_(active_);
+  }
+}
+
 }  // namespace internal
 }  // namespace crashpad