// Copyright 2018 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 "components/crash/core/app/crashpad.h"

#include <dlfcn.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include <algorithm>

#include "base/android/build_info.h"
#include "base/android/java_exception_reporter.h"
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/path_utils.h"
#include "base/environment.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/global_descriptors.h"
#include "base/rand_util.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "components/crash/android/jni_headers/PackagePaths_jni.h"
#include "components/crash/core/app/crash_reporter_client.h"
#include "sandbox/linux/services/syscall_wrappers.h"
#include "services/service_manager/embedder/descriptors.h"
#include "third_party/crashpad/crashpad/client/annotation.h"
#include "third_party/crashpad/crashpad/client/client_argv_handling.h"
#include "third_party/crashpad/crashpad/client/crashpad_client.h"
#include "third_party/crashpad/crashpad/client/simulate_crash_linux.h"
#include "third_party/crashpad/crashpad/snapshot/sanitized/sanitization_information.h"
#include "third_party/crashpad/crashpad/util/linux/exception_handler_client.h"
#include "third_party/crashpad/crashpad/util/linux/exception_handler_protocol.h"
#include "third_party/crashpad/crashpad/util/linux/exception_information.h"
#include "third_party/crashpad/crashpad/util/linux/scoped_pr_set_dumpable.h"
#include "third_party/crashpad/crashpad/util/misc/from_pointer_cast.h"
#include "third_party/crashpad/crashpad/util/posix/signals.h"

namespace crashpad {
namespace {

class MemoryRangeWhitelist {
 public:
  MemoryRangeWhitelist() {
    whitelist_.entries = 0;
    whitelist_.size = 0;
  }

  void AddEntry(VMAddress base, VMSize length) {
    SanitizationMemoryRangeWhitelist::Range new_entry;
    new_entry.base = base;
    new_entry.length = length;

    base::AutoLock lock(lock_);
    std::vector<SanitizationMemoryRangeWhitelist::Range> new_array(array_);
    new_array.push_back(new_entry);
    whitelist_.entries = FromPointerCast<VMAddress>(new_array.data());
    whitelist_.size += 1;
    array_ = std::move(new_array);
  }

  SanitizationMemoryRangeWhitelist* GetSanitizationAddress() {
    return &whitelist_;
  }

  static MemoryRangeWhitelist* Singleton() {
    static base::NoDestructor<MemoryRangeWhitelist> singleton;
    return singleton.get();
  }

 private:
  base::Lock lock_;
  SanitizationMemoryRangeWhitelist whitelist_;
  std::vector<SanitizationMemoryRangeWhitelist::Range> array_;

  DISALLOW_COPY_AND_ASSIGN(MemoryRangeWhitelist);
};

bool SetSanitizationInfo(crash_reporter::CrashReporterClient* client,
                         SanitizationInformation* info) {
  const char* const* whitelist = nullptr;
  void* target_module = nullptr;
  bool sanitize_stacks = false;
  client->GetSanitizationInformation(&whitelist, &target_module,
                                     &sanitize_stacks);
  info->annotations_whitelist_address = FromPointerCast<VMAddress>(whitelist);
  info->target_module_address = FromPointerCast<VMAddress>(target_module);
  info->memory_range_whitelist_address = FromPointerCast<VMAddress>(
      MemoryRangeWhitelist::Singleton()->GetSanitizationAddress());
  info->sanitize_stacks = sanitize_stacks;
  return whitelist != nullptr || target_module != nullptr || sanitize_stacks;
}

void SetExceptionInformation(siginfo_t* siginfo,
                             ucontext_t* context,
                             ExceptionInformation* info) {
  info->siginfo_address =
      FromPointerCast<decltype(info->siginfo_address)>(siginfo);
  info->context_address =
      FromPointerCast<decltype(info->context_address)>(context);
  info->thread_id = sandbox::sys_gettid();
}

void SetClientInformation(ExceptionInformation* exception,
                          SanitizationInformation* sanitization,
                          ExceptionHandlerProtocol::ClientInformation* info) {
  info->exception_information_address =
      FromPointerCast<decltype(info->exception_information_address)>(exception);

  info->sanitization_information_address =
      FromPointerCast<decltype(info->sanitization_information_address)>(
          sanitization);
}

// A signal handler for non-browser processes in the sandbox.
// Sends a message to a crashpad::CrashHandlerHost to handle the crash.
class SandboxedHandler {
 public:
  static SandboxedHandler* Get() {
    static SandboxedHandler* instance = new SandboxedHandler();
    return instance;
  }

  bool Initialize(bool dump_at_crash) {
    request_dump_ = dump_at_crash ? 1 : 0;

    SetSanitizationInfo(crash_reporter::GetCrashReporterClient(),
                        &sanitization_);
    server_fd_ = base::GlobalDescriptors::GetInstance()->Get(
        service_manager::kCrashDumpSignal);

    // Android's debuggerd handler on JB MR2 until OREO displays a dialog which
    // is a bad user experience for child process crashes. Disable the debuggerd
    // handler for user builds. crbug.com/273706
    base::android::BuildInfo* build_info =
        base::android::BuildInfo::GetInstance();
    restore_previous_handler_ =
        build_info->sdk_int() < base::android::SDK_VERSION_JELLY_BEAN_MR2 ||
        build_info->sdk_int() >= base::android::SDK_VERSION_OREO ||
        strcmp(build_info->build_type(), "eng") == 0 ||
        strcmp(build_info->build_type(), "userdebug") == 0;

    return Signals::InstallCrashHandlers(HandleCrash, 0, &old_actions_);
  }

  void HandleCrashNonFatal(int signo, siginfo_t* siginfo, void* context) {
    base::ScopedFD connection;
    if (ConnectToHandler(signo, &connection) == 0) {
      ExceptionInformation exception_information;
      SetExceptionInformation(siginfo, static_cast<ucontext_t*>(context),
                              &exception_information);

      ExceptionHandlerProtocol::ClientInformation info;
      SetClientInformation(&exception_information, &sanitization_, &info);

      ScopedPrSetDumpable set_dumpable(/* may_log= */ false);

      ExceptionHandlerClient handler_client(connection.get(), false);
      handler_client.SetCanSetPtracer(false);
      handler_client.RequestCrashDump(info);
    }
  }

 private:
  SandboxedHandler() = default;
  ~SandboxedHandler() = delete;

  int ConnectToHandler(int signo, base::ScopedFD* connection) {
    int fds[2];
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0) {
      return errno;
    }
    base::ScopedFD local_connection(fds[0]);
    base::ScopedFD handlers_socket(fds[1]);

    // SELinux may block the handler from setting SO_PASSCRED on this socket.
    // Attempt to set it here, but the handler can still try if this fails.
    int optval = 1;
    socklen_t optlen = sizeof(optval);
    setsockopt(handlers_socket.get(), SOL_SOCKET, SO_PASSCRED, &optval, optlen);

    iovec iov[2];
    iov[0].iov_base = &signo;
    iov[0].iov_len = sizeof(signo);
    iov[1].iov_base = &request_dump_;
    iov[1].iov_len = sizeof(request_dump_);

    msghdr msg;
    msg.msg_name = nullptr;
    msg.msg_namelen = 0;
    msg.msg_iov = iov;
    msg.msg_iovlen = base::size(iov);

    char cmsg_buf[CMSG_SPACE(sizeof(int))];
    msg.msg_control = cmsg_buf;
    msg.msg_controllen = sizeof(cmsg_buf);

    cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
    cmsg->cmsg_level = SOL_SOCKET;
    cmsg->cmsg_type = SCM_RIGHTS;
    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
    *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = handlers_socket.get();

    if (HANDLE_EINTR(sendmsg(server_fd_, &msg, MSG_NOSIGNAL)) < 0) {
      return errno;
    }

    *connection = std::move(local_connection);
    return 0;
  }

  static void HandleCrash(int signo, siginfo_t* siginfo, void* context) {
    SandboxedHandler* state = Get();
    state->HandleCrashNonFatal(signo, siginfo, context);
    Signals::RestoreHandlerAndReraiseSignalOnReturn(
        siginfo, state->restore_previous_handler_
                     ? state->old_actions_.ActionForSignal(signo)
                     : nullptr);
  }

  Signals::OldActions old_actions_ = {};
  SanitizationInformation sanitization_;
  int server_fd_;
  unsigned char request_dump_;

  // true if the previously installed signal handler is restored after
  // handling a crash. Otherwise SIG_DFL is restored.
  bool restore_previous_handler_;

  DISALLOW_COPY_AND_ASSIGN(SandboxedHandler);
};

}  // namespace
}  // namespace crashpad

namespace crash_reporter {
namespace {

void SetJavaExceptionInfo(const char* info_string) {
  static crashpad::StringAnnotation<5 * 4096> exception_info("exception_info");
  if (info_string) {
    exception_info.Set(info_string);
  } else {
    exception_info.Clear();
  }
}

void SetBuildInfoAnnotations(std::map<std::string, std::string>* annotations) {
  base::android::BuildInfo* info = base::android::BuildInfo::GetInstance();

  (*annotations)["android_build_id"] = info->android_build_id();
  (*annotations)["android_build_fp"] = info->android_build_fp();
  (*annotations)["sdk"] = base::StringPrintf("%d", info->sdk_int());
  (*annotations)["device"] = info->device();
  (*annotations)["model"] = info->model();
  (*annotations)["brand"] = info->brand();
  (*annotations)["board"] = info->board();
  (*annotations)["installer_package_name"] = info->installer_package_name();
  (*annotations)["abi_name"] = info->abi_name();
  (*annotations)["custom_themes"] = info->custom_themes();
  (*annotations)["resources_version"] = info->resources_version();
  (*annotations)["gms_core_version"] = info->gms_version_code();

  if (info->firebase_app_id()[0] != '\0') {
    (*annotations)["package"] = std::string(info->firebase_app_id()) + " v" +
                                info->package_version_code() + " (" +
                                info->package_version_name() + ")";
  }
}

// Constructs paths to a handler trampoline executable and a library exporting
// the symbol `CrashpadHandlerMain()`. This requires this function to be built
// into the same object exporting this symbol and the handler trampoline is
// adjacent to it.
bool GetHandlerTrampoline(std::string* handler_trampoline,
                          std::string* handler_library) {
  // The linker doesn't support loading executables passed on its command
  // line until Q.
  if (!base::android::BuildInfo::GetInstance()->is_at_least_q()) {
    return false;
  }

  Dl_info info;
  if (dladdr(reinterpret_cast<void*>(&GetHandlerTrampoline), &info) == 0 ||
      dlsym(dlopen(info.dli_fname, RTLD_NOLOAD | RTLD_LAZY),
            "CrashpadHandlerMain") == nullptr) {
    return false;
  }

  std::string local_handler_library(info.dli_fname);

  size_t libdir_end = local_handler_library.rfind('/');
  if (libdir_end == std::string::npos) {
    return false;
  }

  std::string local_handler_trampoline(local_handler_library, 0,
                                       libdir_end + 1);
  local_handler_trampoline += "libcrashpad_handler_trampoline.so";

  handler_trampoline->swap(local_handler_trampoline);
  handler_library->swap(local_handler_library);
  return true;
}

#if defined(__arm__) && defined(__ARM_ARCH_7A__)
#define CURRENT_ABI "armeabi-v7a"
#elif defined(__arm__)
#define CURRENT_ABI "armeabi"
#elif defined(__i386__)
#define CURRENT_ABI "x86"
#elif defined(__mips__)
#define CURRENT_ABI "mips"
#elif defined(__x86_64__)
#define CURRENT_ABI "x86_64"
#elif defined(__aarch64__)
#define CURRENT_ABI "arm64-v8a"
#else
#error "Unsupported target abi"
#endif

void MakePackagePaths(std::string* classpath, std::string* libpath) {
  JNIEnv* env = base::android::AttachCurrentThread();

  base::android::ScopedJavaLocalRef<jstring> arch =
      base::android::ConvertUTF8ToJavaString(env,
                                             base::StringPiece(CURRENT_ABI));
  base::android::ScopedJavaLocalRef<jobjectArray> paths =
      Java_PackagePaths_makePackagePaths(env, arch);

  base::android::ConvertJavaStringToUTF8(
      env, static_cast<jstring>(env->GetObjectArrayElement(paths.obj(), 0)),
      classpath);
  base::android::ConvertJavaStringToUTF8(
      env, static_cast<jstring>(env->GetObjectArrayElement(paths.obj(), 1)),
      libpath);
}

// Copies and extends the current environment with CLASSPATH and LD_LIBRARY_PATH
// set to library paths in the APK.
bool BuildEnvironmentWithApk(bool use_64_bit,
                             std::vector<std::string>* result) {
  DCHECK(result->empty());

  std::string classpath;
  std::string library_path;
  MakePackagePaths(&classpath, &library_path);

  std::unique_ptr<base::Environment> env(base::Environment::Create());
  static constexpr char kClasspathVar[] = "CLASSPATH";
  std::string current_classpath;
  env->GetVar(kClasspathVar, &current_classpath);
  classpath += ":" + current_classpath;

  static constexpr char kLdLibraryPathVar[] = "LD_LIBRARY_PATH";
  std::string current_library_path;
  env->GetVar(kLdLibraryPathVar, &current_library_path);
  library_path += ":" + current_library_path;

  static constexpr char kRuntimeRootVar[] = "ANDROID_RUNTIME_ROOT";
  std::string runtime_root;
  if (env->GetVar(kRuntimeRootVar, &runtime_root)) {
    library_path += ":" + runtime_root + (use_64_bit ? "/lib64" : "/lib");
  }

  result->push_back("CLASSPATH=" + classpath);
  result->push_back("LD_LIBRARY_PATH=" + library_path);
  for (char** envp = environ; *envp != nullptr; ++envp) {
    if ((strncmp(*envp, kClasspathVar, strlen(kClasspathVar)) == 0 &&
         (*envp)[strlen(kClasspathVar)] == '=') ||
        (strncmp(*envp, kLdLibraryPathVar, strlen(kLdLibraryPathVar)) == 0 &&
         (*envp)[strlen(kLdLibraryPathVar)] == '=')) {
      continue;
    }
    result->push_back(*envp);
  }

  return true;
}

const char kCrashpadJavaMain[] =
    "org.chromium.components.crash.browser.CrashpadMain";

void BuildHandlerArgs(CrashReporterClient* crash_reporter_client,
                      base::FilePath* database_path,
                      base::FilePath* metrics_path,
                      std::string* url,
                      std::map<std::string, std::string>* process_annotations,
                      std::vector<std::string>* arguments) {
  crash_reporter_client->GetCrashDumpLocation(database_path);
  crash_reporter_client->GetCrashMetricsLocation(metrics_path);

  // TODO(jperaza): Set URL for Android when Crashpad takes over report upload.
  *url = std::string();

  std::string product_name;
  std::string product_version;
  std::string channel;
  crash_reporter_client->GetProductNameAndVersion(&product_name,
                                                  &product_version, &channel);
  (*process_annotations)["prod"] = product_name;
  (*process_annotations)["ver"] = product_version;

  SetBuildInfoAnnotations(process_annotations);

#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  // Empty means stable.
  const bool allow_empty_channel = true;
#else
  const bool allow_empty_channel = false;
#endif
  if (allow_empty_channel || !channel.empty()) {
    (*process_annotations)["channel"] = channel;
  }

  (*process_annotations)["plat"] = std::string("Android");
}

bool ShouldHandleCrashAndUpdateArguments(bool write_minidump_to_database,
                                         bool write_minidump_to_log,
                                         std::vector<std::string>* arguments) {
  if (!write_minidump_to_database)
    arguments->push_back("--no-write-minidump-to-database");
  if (write_minidump_to_log)
    arguments->push_back("--write-minidump-to-log");
  return write_minidump_to_database || write_minidump_to_log;
}

bool GetHandlerPath(base::FilePath* exe_dir, base::FilePath* handler_path) {
  // There is not any normal way to package native executables in an Android
  // APK. The Crashpad handler is packaged like a loadable module, which
  // Android's APK installer expects to be named like a shared library, but it
  // is in fact a standalone executable.
  if (!base::PathService::Get(base::DIR_MODULE, exe_dir)) {
    return false;
  }
  *handler_path = exe_dir->Append("libchrome_crashpad_handler.so");
  return true;
}

bool SetLdLibraryPath(const base::FilePath& lib_path) {
#if defined(COMPONENT_BUILD)
  std::string library_path(lib_path.value());

  static constexpr char kLibraryPathVar[] = "LD_LIBRARY_PATH";
  std::unique_ptr<base::Environment> env(base::Environment::Create());
  std::string old_path;
  if (env->GetVar(kLibraryPathVar, &old_path)) {
    library_path.push_back(':');
    library_path.append(old_path);
  }

  if (!env->SetVar(kLibraryPathVar, library_path)) {
    return false;
  }
#endif

  return true;
}

class HandlerStarter {
  // TODO(jperaza): Currently only launching a same-bitness handler is
  // supported. The logic to build package paths, locate a handler executable,
  // and the crashpad client interface for launching a Java handler need to be
  // updated to use a specified bitness before a cross-bitness handler can be
  // used.
#if defined(ARCH_CPU_64_BITS)
  static constexpr bool kUse64Bit = true;
#else
  static constexpr bool kUse64Bit = false;
#endif

 public:
  static HandlerStarter* Get() {
    static HandlerStarter* instance = new HandlerStarter();
    return instance;
  }

  base::FilePath Initialize(bool dump_at_crash) {
    base::FilePath database_path;
    base::FilePath metrics_path;
    std::string url;
    std::map<std::string, std::string> process_annotations;
    std::vector<std::string> arguments;
    BuildHandlerArgs(GetCrashReporterClient(), &database_path, &metrics_path,
                     &url, &process_annotations, &arguments);

    base::FilePath exe_dir;
    base::FilePath handler_path;
    if (!GetHandlerPath(&exe_dir, &handler_path)) {
      return database_path;
    }

    if (crashpad::SetSanitizationInfo(GetCrashReporterClient(),
                                      &browser_sanitization_info_)) {
      arguments.push_back(base::StringPrintf("--sanitization-information=%p",
                                             &browser_sanitization_info_));
    }

    std::string browser_ptype;
    if (GetCrashReporterClient()->GetBrowserProcessType(&browser_ptype)) {
      process_annotations["ptype"] = browser_ptype;
    }

    if (!base::PathExists(handler_path)) {
      use_java_handler_ =
          !GetHandlerTrampoline(&handler_trampoline_, &handler_library_);
    }

    if (!ShouldHandleCrashAndUpdateArguments(
            dump_at_crash, GetCrashReporterClient()->ShouldWriteMinidumpToLog(),
            &arguments)) {
      return database_path;
    }

    if (use_java_handler_ || !handler_trampoline_.empty()) {
      std::vector<std::string> env;
      if (!BuildEnvironmentWithApk(kUse64Bit, &env)) {
        return database_path;
      }

      bool result = use_java_handler_
                        ? GetCrashpadClient().StartJavaHandlerAtCrash(
                              kCrashpadJavaMain, &env, database_path,
                              metrics_path, url, process_annotations, arguments)
                        : GetCrashpadClient().StartHandlerWithLinkerAtCrash(
                              handler_trampoline_, handler_library_, kUse64Bit,
                              &env, database_path, metrics_path, url,
                              process_annotations, arguments);
      DCHECK(result);
      return database_path;
    }

    if (!SetLdLibraryPath(exe_dir)) {
      return database_path;
    }

    bool result = GetCrashpadClient().StartHandlerAtCrash(
        handler_path, database_path, metrics_path, url, process_annotations,
        arguments);
    DCHECK(result);
    return database_path;
  }

  bool StartHandlerForClient(CrashReporterClient* client,
                             int fd,
                             bool write_minidump_to_database) {
    base::FilePath database_path;
    base::FilePath metrics_path;
    std::string url;
    std::map<std::string, std::string> process_annotations;
    std::vector<std::string> arguments;
    BuildHandlerArgs(client, &database_path, &metrics_path, &url,
                     &process_annotations, &arguments);

    base::FilePath exe_dir;
    base::FilePath handler_path;
    if (!GetHandlerPath(&exe_dir, &handler_path)) {
      return false;
    }

    if (!ShouldHandleCrashAndUpdateArguments(write_minidump_to_database,
                                             client->ShouldWriteMinidumpToLog(),
                                             &arguments)) {
      return true;
    }

    if (use_java_handler_ || !handler_trampoline_.empty()) {
      std::vector<std::string> env;
      if (!BuildEnvironmentWithApk(kUse64Bit, &env)) {
        return false;
      }

      bool result =
          use_java_handler_
              ? GetCrashpadClient().StartJavaHandlerForClient(
                    kCrashpadJavaMain, &env, database_path, metrics_path, url,
                    process_annotations, arguments, fd)
              : GetCrashpadClient().StartHandlerWithLinkerForClient(
                    handler_trampoline_, handler_library_, kUse64Bit, &env,
                    database_path, metrics_path, url, process_annotations,
                    arguments, fd);
      return result;
    }

    if (!SetLdLibraryPath(exe_dir)) {
      return false;
    }

    return GetCrashpadClient().StartHandlerForClient(
        handler_path, database_path, metrics_path, url, process_annotations,
        arguments, fd);
  }

 private:
  HandlerStarter() = default;
  ~HandlerStarter() = delete;

  crashpad::SanitizationInformation browser_sanitization_info_;
  std::string handler_trampoline_;
  std::string handler_library_;
  bool use_java_handler_ = false;

  DISALLOW_COPY_AND_ASSIGN(HandlerStarter);
};

bool ConnectToHandler(CrashReporterClient* client, base::ScopedFD* connection) {
  int fds[2];
  if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0) {
    PLOG(ERROR) << "socketpair";
    return false;
  }
  base::ScopedFD local_connection(fds[0]);
  base::ScopedFD handlers_socket(fds[1]);

  if (!HandlerStarter::Get()->StartHandlerForClient(
          client, handlers_socket.get(),
          true /* write_minidump_to_database */)) {
    return false;
  }

  *connection = std::move(local_connection);
  return true;
}

bool g_is_browser = false;

}  // namespace

// TODO(jperaza): This might be simplified to have both the browser and child
// processes use CRASHPAD_SIMULATE_CRASH() if CrashpadClient allows injecting
// the Chromium specific SandboxedHandler.
void DumpWithoutCrashing() {
  if (g_is_browser) {
    CRASHPAD_SIMULATE_CRASH();
  } else {
    siginfo_t siginfo;
    siginfo.si_signo = crashpad::Signals::kSimulatedSigno;
    siginfo.si_errno = 0;
    siginfo.si_code = 0;

    ucontext_t context;
    crashpad::CaptureContext(&context);

    crashpad::SandboxedHandler::Get()->HandleCrashNonFatal(siginfo.si_signo,
                                                           &siginfo, &context);
  }
}

bool DumpWithoutCrashingForClient(CrashReporterClient* client) {
  base::ScopedFD connection;
  if (!ConnectToHandler(client, &connection)) {
    return false;
  }

  siginfo_t siginfo;
  siginfo.si_signo = crashpad::Signals::kSimulatedSigno;
  siginfo.si_errno = 0;
  siginfo.si_code = 0;

  ucontext_t context;
  crashpad::CaptureContext(&context);

  crashpad::SanitizationInformation sanitization;
  crashpad::SetSanitizationInfo(client, &sanitization);

  crashpad::ExceptionInformation exception;
  crashpad::SetExceptionInformation(&siginfo, &context, &exception);

  crashpad::ExceptionHandlerProtocol::ClientInformation info;
  crashpad::SetClientInformation(&exception, &sanitization, &info);

  crashpad::ScopedPrSetDumpable set_dumpable(/* may_log= */ false);

  crashpad::ExceptionHandlerClient handler_client(connection.get(), false);
  return handler_client.RequestCrashDump(info) == 0;
}

void WhitelistMemoryRange(void* begin, size_t length) {
  crashpad::MemoryRangeWhitelist::Singleton()->AddEntry(
      crashpad::FromPointerCast<crashpad::VMAddress>(begin),
      static_cast<crashpad::VMSize>(length));
}

namespace internal {

bool StartHandlerForClient(int fd, bool write_minidump_to_database) {
  return HandlerStarter::Get()->StartHandlerForClient(
      GetCrashReporterClient(), fd, write_minidump_to_database);
}

base::FilePath PlatformCrashpadInitialization(
    bool initial_client,
    bool browser_process,
    bool embedded_handler,
    const std::string& user_data_dir,
    const base::FilePath& exe_path,
    const std::vector<std::string>& initial_arguments) {
  DCHECK_EQ(initial_client, browser_process);
  DCHECK(initial_arguments.empty());

  // Not used on Android.
  DCHECK(!embedded_handler);
  DCHECK(exe_path.empty());

  g_is_browser = browser_process;

  bool dump_at_crash = true;
  base::android::SetJavaExceptionCallback(SetJavaExceptionInfo);

  unsigned int dump_percentage =
      GetCrashReporterClient()->GetCrashDumpPercentage();
  if (dump_percentage < 100 &&
      static_cast<unsigned int>(base::RandInt(0, 99)) >= dump_percentage) {
    dump_at_crash = false;
  }

  if (browser_process) {
    HandlerStarter* starter = HandlerStarter::Get();
    return starter->Initialize(dump_at_crash);
  }

  crashpad::SandboxedHandler* handler = crashpad::SandboxedHandler::Get();
  bool result = handler->Initialize(dump_at_crash);
  DCHECK(result);

  return base::FilePath();
}

}  // namespace internal

}  // namespace crash_reporter
