// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stddef.h>
#include <stdlib.h>

#include <memory>
#include <tuple>
#include <utility>

#include "base/allocator/partition_alloc_support.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/files/scoped_file.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/message_loop/message_pump_type.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/clamped_math.h"
#include "base/process/current_process.h"
#include "base/process/process_metrics.h"
#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/system/sys_info.h"
#include "base/task/current_thread.h"
#include "base/task/single_thread_task_executor.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/threading/hang_watcher.h"
#include "base/threading/platform_thread.h"
#include "base/threading/platform_thread_metrics.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "base/timer/hi_res_timer_manager.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "components/viz/common/features.h"
#include "components/viz/service/gl/gpu_log_message_manager.h"
#include "components/viz/service/main/viz_main_impl.h"
#include "content/child/child_process.h"
#include "content/common/content_constants_internal.h"
#include "content/common/content_switches_internal.h"
#include "content/common/features.h"
#include "content/common/skia_utils.h"
#include "content/gpu/gpu_child_thread.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/main_function_params.h"
#include "content/public/common/result_codes.h"
#include "content/public/common/zygote/zygote_buildflags.h"
#include "content/public/gpu/content_gpu_client.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/config/gpu_driver_bug_list.h"
#include "gpu/config/gpu_finch_features.h"
#include "gpu/config/gpu_info_collector.h"
#include "gpu/config/gpu_preferences.h"
#include "gpu/config/gpu_switches.h"
#include "gpu/config/gpu_util.h"
#include "gpu/ipc/service/gpu_config.h"
#include "gpu/ipc/service/gpu_init.h"
#include "gpu/ipc/service/gpu_watchdog_thread.h"
#include "media/gpu/buildflags.h"
#include "mojo/public/cpp/bindings/direct_receiver.h"
#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
#include "services/tracing/public/cpp/trace_startup.h"
#include "services/tracing/public/cpp/trace_startup_config.h"
#include "third_party/angle/src/gpu_info_util/SystemInfo.h"
#include "ui/base/ui_base_features.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/gfx/switches.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_features.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/gl_switches.h"
#include "ui/gl/gpu_switching_manager.h"
#include "ui/gl/init/gl_factory.h"

#if BUILDFLAG(IS_WIN)
#include <windows.h>

#include <dwmapi.h>
#endif

#if BUILDFLAG(IS_ANDROID)
#include "base/android/meminfo_dump_provider.h"
#include "base/posix/eintr_wrapper.h"
#include "base/trace_event/memory_dump_manager.h"
#include "components/tracing/common/graphics_memory_dump_provider_android.h"
#include "sandbox/linux/services/thread_helpers.h" // nogncheck
#include "sandbox/policy/features.h"
#include "sandbox/policy/linux/landlock_gpu_policy_android.h"
#include "sandbox/policy/sandbox_type.h"
#endif

#if BUILDFLAG(IS_WIN)
#include "base/win/scoped_com_initializer.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "media/base/win/mf_initializer.h"
#include "sandbox/policy/win/sandbox_warmup.h"
#include "sandbox/win/src/sandbox.h"
#endif

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include "content/child/sandboxed_process_thread_type_handler.h"
#include "content/common/gpu_pre_sandbox_hook_linux.h"
#include "sandbox/policy/linux/sandbox_linux.h"
#include "sandbox/policy/sandbox_type.h"
#endif

#if BUILDFLAG(IS_MAC)
#include "base/message_loop/message_pump_apple.h"
#include "components/metal_util/device_removal.h"
#include "sandbox/mac/seatbelt.h"
#endif

#if BUILDFLAG(USE_VAAPI)
#include "media/gpu/vaapi/vaapi_wrapper.h"
#endif

#if BUILDFLAG(IS_OHOS)
#include "content/common/gpu_pre_sandbox_hook_linux.h"
#include "sandbox/policy/linux/sandbox_linux.h"
#include "sandbox/policy/sandbox_type.h"
#endif

#if BUILDFLAG(IS_ARKWEB)
#include "arkweb/chromium_ext/content/gpu/gpu_main_ext.h"
#endif

namespace content {

namespace {

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
bool StartSandboxLinux(gpu::GpuWatchdogThread*,
                       const gpu::GPUInfo*,
                       const gpu::GpuPreferences&);
#elif BUILDFLAG(IS_ANDROID)
bool StartSandboxAndroid(gpu::GpuWatchdogThread*);
#elif BUILDFLAG(IS_WIN)
bool StartSandboxWindows(const sandbox::SandboxInterfaceInfo*);
#elif BUILDFLAG(IS_OHOS)
bool StartSandboxOHOS(gpu::GpuWatchdogThread*);
#endif

class ContentSandboxHelper : public gpu::GpuSandboxHelper {
 public:
  ContentSandboxHelper() {}

  ContentSandboxHelper(const ContentSandboxHelper&) = delete;
  ContentSandboxHelper& operator=(const ContentSandboxHelper&) = delete;

  ~ContentSandboxHelper() override {}

#if BUILDFLAG(IS_WIN)
  void set_sandbox_info(const sandbox::SandboxInterfaceInfo* info) {
    sandbox_info_ = info;
  }
#endif

 private:
  // SandboxHelper:
  void PreSandboxStartup(const gpu::GpuPreferences& gpu_prefs) override {
    TRACE_EVENT("gpu,startup", "gpu_main::PreSandboxStartup");
    // Warm up resources that don't need access to GPUInfo.
    {
      TRACE_EVENT0("gpu", "Warm up rand");
      // Warm up the random subsystem, which needs to be done pre-sandbox on all
      // platforms.
#if BUILDFLAG(IS_WIN)
      sandbox::policy::WarmupRandomnessInfrastructure();
#else
      std::ignore = base::RandUint64();
#endif  // BUILDFLAG(IS_WIN)
    }

#if BUILDFLAG(USE_VAAPI)
#if BUILDFLAG(IS_CHROMEOS)
    media::VaapiWrapper::PreSandboxInitialization();
#else  // For Linux with VA-API support.
    if (!gpu_prefs.disable_accelerated_video_decode)
      media::VaapiWrapper::PreSandboxInitialization();
#endif
#endif  // BUILDFLAG(USE_VAAPI)
#if BUILDFLAG(IS_WIN)
    media::PreSandboxMediaFoundationInitialization();
#endif

    // On Linux, reading system memory doesn't work through the GPU sandbox.
    // This value is cached, so access it here to populate the cache.
    base::SysInfo::AmountOfPhysicalMemory();
  }

  bool EnsureSandboxInitialized(gpu::GpuWatchdogThread* watchdog_thread,
                                const gpu::GPUInfo* gpu_info,
                                const gpu::GpuPreferences& gpu_prefs) override {
    TRACE_EVENT("gpu,startup", "gpu_main::EnsureSandboxInitialized");
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
    return StartSandboxLinux(watchdog_thread, gpu_info, gpu_prefs);
#elif BUILDFLAG(IS_WIN)
    return StartSandboxWindows(sandbox_info_);
#elif BUILDFLAG(IS_MAC)
    return sandbox::Seatbelt::IsSandboxed();
#elif BUILDFLAG(IS_OHOS)
#if BUILDFLAG(ARKWEB_OOP_GPU_PROCESS)
    return false;
#else
    return StartSandboxOHOS(watchdog_thread);
#endif
#elif BUILDFLAG(IS_ANDROID)
    if (base::FeatureList::IsEnabled(
            sandbox::policy::features::kAndroidGpuSandbox)) {
      return StartSandboxAndroid(watchdog_thread);
    }
    return false;
#else
    return false;
#endif
  }

#if BUILDFLAG(IS_WIN)
  raw_ptr<const sandbox::SandboxInterfaceInfo> sandbox_info_ = nullptr;
#endif
};

}  // namespace

// Main function for starting the Gpu process.
int GpuMain(MainFunctionParams parameters) {
  TRACE_EVENT("gpu,startup", "GpuMain");

  base::CurrentProcess::GetInstance().SetProcessType(
      base::CurrentProcessType::PROCESS_GPU);

  const base::CommandLine& command_line = *parameters.command_line;

  gpu::GpuPreferences gpu_preferences;
  if (command_line.HasSwitch(switches::kGpuPreferences)) {
    std::string value =
        command_line.GetSwitchValueASCII(switches::kGpuPreferences);
    bool success = gpu_preferences.FromSwitchValue(value);
    CHECK(success);
  }

  // Disallow sending sync IPCs from the GPU process, in particular CrGpuMain
  // and VizCompositorThreads. Incoming sync IPCs can be received out of order
  // when waiting on response to an outgoing sync IPC. Both viz and gpu
  // interfaces rely on receiving messages in order so this message reordering
  // would break things.
  mojo::SyncCallRestrictions::DisallowSyncCall();

  if (gpu_preferences.gpu_startup_dialog)
    WaitForDebugger("Gpu");

  base::TimeTicks start_time = base::TimeTicks::Now();

#if BUILDFLAG(IS_WIN)
  base::win::EnableHighDPISupport();

  // Prevent Windows from displaying a modal dialog on failures like not being
  // able to load a DLL.
  SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
               SEM_NOOPENFILEERRORBOX);

  // Disable high resolution timer throttling to prevent the OS from degrading
  // performance.
  base::win::SetProcessTimerThrottleState(
      base::GetCurrentProcessHandle(), base::win::ProcessPowerState::kDisabled);

  // COM is used by some Windows Media Foundation calls made on this thread and
  // must be MTA so we don't have to worry about pumping messages to handle
  // COM callbacks.
  base::win::ScopedCOMInitializer com_initializer(
      base::win::ScopedCOMInitializer::kMTA);

  // A higher priority class is used for the GPU process so that it remains at
  // a higher priority than renderer processes.
  ::SetPriorityClass(::GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
#endif

  // Installs a base::LogMessageHandlerFunction which ensures messages are sent
  // to the GpuProcessHost once the GpuServiceImpl has started.
  viz::GpuLogMessageManager::GetInstance()->InstallPreInitializeLogHandler();

  // We are experiencing what appear to be memory-stomp issues in the GPU
  // process. These issues seem to be impacting the task executor and listeners
  // registered to it. Create the task executor on the heap to guard against
  // this.
  // TODO(ericrk): Revisit this once we assess its impact on crbug.com/662802
  // and crbug.com/609252.
  std::unique_ptr<base::SingleThreadTaskExecutor> main_thread_task_executor;
  std::unique_ptr<ui::PlatformEventSource> event_source;
  if (command_line.HasSwitch(switches::kHeadless)) {
#if BUILDFLAG(IS_MAC)
    // CADisplayLink (Mac HW VSync) callback only works with NS_RUNLOOP.
    main_thread_task_executor =
        std::make_unique<base::SingleThreadTaskExecutor>(
            base::MessagePumpType::NS_RUNLOOP, /*is_main_thread=*/true);
    main_thread_task_executor->SetWorkBatchSize(2);
#else
    main_thread_task_executor =
        std::make_unique<base::SingleThreadTaskExecutor>(
            base::MessagePumpType::DEFAULT, /*is_main_thread=*/true);
#endif
  } else {
#if BUILDFLAG(IS_WIN)
    // The GpuMain thread should not be pumping Windows messages because no UI
    // is expected to run on this thread.
    main_thread_task_executor =
        std::make_unique<base::SingleThreadTaskExecutor>(
            base::MessagePumpType::DEFAULT, /*is_main_thread=*/true);
#elif BUILDFLAG(IS_OZONE)
    // The MessagePump type required depends on the Ozone platform selected at
    // runtime.
    if (!main_thread_task_executor) {
      main_thread_task_executor =
          std::make_unique<base::SingleThreadTaskExecutor>(
              gpu_preferences.message_pump_type, /*is_main_thread=*/true);
    }
#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#error "Unsupported Linux platform."
#elif BUILDFLAG(IS_MAC)
    // Cross-process CoreAnimation requires a CFRunLoop to function at all, and
    // requires a NSRunLoop to not starve under heavy load. See:
    // https://crbug.com/312462#c51 and https://crbug.com/783298
    // CADisplayLink (Mac HW VSync) callback only works with NS_RUNLOOP. DEFAULT
    // type does not support NSObject.
    main_thread_task_executor =
        std::make_unique<base::SingleThreadTaskExecutor>(
            base::MessagePumpType::NS_RUNLOOP, /*is_main_thread=*/true);
    // As part of the migration to DoWork(), this policy is required to keep
    // previous behavior and avoid regressions.
    // TODO(crbug.com/40668161): Consider updating the policy.
    main_thread_task_executor->SetWorkBatchSize(2);
#else
    main_thread_task_executor =
        std::make_unique<base::SingleThreadTaskExecutor>(
            base::MessagePumpType::DEFAULT, /*is_main_thread=*/true);
#endif
  }

  base::PlatformThread::SetName("CrGpuMain");
  mojo::InterfaceEndpointClient::SetThreadNameSuffixForMetrics("GpuMain");

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  // Thread type delegate of the process should be registered before
  // thread type change below for the main thread and for thread pool in
  // ChildProcess constructor.
  // It also needs to be registered before the process has multiple threads,
  // which may race with application of the sandbox. InitializeAndStartSandbox()
  // sandboxes the process and starts threads so this has to happen first.
  SandboxedProcessThreadTypeHandler::Create();
#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)

  base::PlatformThread::SetCurrentThreadType(
      base::ThreadType::kDisplayCritical);

  auto gpu_init = std::make_unique<gpu::GpuInit>();
  ContentSandboxHelper sandbox_helper;
#if BUILDFLAG(IS_WIN)
  sandbox_helper.set_sandbox_info(parameters.sandbox_info);
#endif

  gpu_init->set_sandbox_helper(&sandbox_helper);

  // Since GPU initialization calls into skia, it's important to initialize skia
  // before it.
  {
    TRACE_EVENT("gpu,startup", "gpu_main::InitializeSkia");
    InitializeSkia();
  }

  // The ThreadPool must have been created before invoking |gpu_init| as it
  // needs the ThreadPool (in angle::InitializePlatform()). Do not start it
  // until after the sandbox is initialized however to avoid creating threads
  // outside the sandbox.
  DCHECK(base::ThreadPoolInstance::Get());

  // Gpu initialization may fail for various reasons, in which case we will need
  // to tear down this process. However, we can not do so safely until the IPC
  // channel is set up, because the detection of early return of a child process
  // is implemented using an IPC channel error. If the IPC channel is not fully
  // set up between the browser and GPU process, and the GPU process crashes or
  // exits early, the browser process will never detect it.  For this reason we
  // defer tearing down the GPU process until receiving the initialization
  // message from the browser (through mojom::VizMain::CreateGpuService()).
  const bool init_success = gpu_init->InitializeAndStartSandbox(
      const_cast<base::CommandLine*>(&command_line), gpu_preferences);
#if BUILDFLAG(IS_CHROMEOS)
  LOG(WARNING) << "gpu initialization completed init_success:" << init_success;
#endif
  const bool dead_on_arrival = !init_success;

  auto* client = GetContentClient()->gpu();
  if (client) {
    client->PostSandboxInitialized();
  }

  // Start the HangWatcher now that the sandbox is engaged, if it hasn't already
  // been started.
  if (base::HangWatcher::IsEnabled() &&
      !base::HangWatcher::GetInstance()->IsStarted()) {
    DCHECK(parameters.hang_watcher_not_started_time.has_value());
    base::TimeDelta uncovered_hang_watcher_time =
        base::TimeTicks::Now() -
        parameters.hang_watcher_not_started_time.value();
    base::UmaHistogramTimes("HangWatcher.GpuProcess.UncoveredStartupTime",
                            uncovered_hang_watcher_time);
    base::HangWatcher::GetInstance()->Start();
  }

#if BUILDFLAG(IS_ANDROID)
  base::PlatformThreadPriorityMonitor::Get().RegisterCurrentThread("GpuMain");
  base::PlatformThreadPriorityMonitor::Get().Start();
#endif  // BUILDFLAG(IS_ANDROID)

  // Startup tracing creates a tracing thread, which is incompatible on
  // platforms that require single-threaded sandbox initialization. In these
  // cases, startup tracing is either initialized right after sandbox
  // initialization, or we restart the tracing thread during sandbox
  // initialization.
  if (parameters.needs_startup_tracing_after_sandbox_init) {
    tracing::InitTracingPostFeatureList(/*enable_consumer=*/false,
                                        /*will_trace_thread_restart=*/false);
  }

  GetContentClient()->SetGpuInfo(gpu_init->gpu_info());

  base::ThreadType io_thread_type = base::ThreadType::kDisplayCritical;
  // ChildProcess will start the ThreadPoolInstance now that the sandbox is
  // initialized.
  ChildProcess gpu_process(io_thread_type);
  DCHECK(base::ThreadPoolInstance::Get()->WasStarted());

  if (client) {
    client->PostIOThreadCreated(gpu_process.io_task_runner());
  }

  base::RunLoop run_loop;
  GpuChildThread* child_thread =
      new GpuChildThread(run_loop.QuitClosure(), std::move(gpu_init));
  child_thread->Init(start_time, main_thread_task_executor->sequence_manager());

  gpu_process.set_main_thread(child_thread);

#if BUILDFLAG(IS_MAC)
  // A GPUEjectPolicy of 'wait' is set in the Info.plist of the browser
  // process, meaning it is "responsible" for making sure it and its
  // subordinate processes (i.e. the GPU process) drop references to the
  // external GPU. Despite this, the system still sends the device removal
  // notifications to the GPU process, so the GPU process handles its own
  // graceful shutdown without help from the browser process.
  //
  // Using the "SafeEjectGPU" tool, we can see that when the browser process
  // has a policy of 'wait', the GPU process gets the 'rwait' policy: "Eject
  // actions apply to the responsible process, who in turn deals with
  // subordinates to eliminate their ejecting eGPU references" [man 8
  // SafeEjectGPU]. Empirically, the browser does not relaunch. Once the GPU
  // process exits, it appears that the browser process is no longer considered
  // to be using the GPU, so it "succeeds" the 'wait'.
  metal::RegisterGracefulExitOnDeviceRemoval();
#endif

#if BUILDFLAG(IS_ANDROID)
  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
      tracing::GraphicsMemoryDumpProvider::GetInstance(), "AndroidGraphics",
      nullptr);

  base::android::MeminfoDumpProvider::Initialize();
#endif

  base::allocator::PartitionAllocSupport::Get()->ReconfigureAfterTaskRunnerInit(
      switches::kGpuProcess);

#if BUILDFLAG(ARKWEB_PERFORMANCE_SCHEDULING)
  retry_times = 0;
  TryForReportThread();
#endif //!BUILDFLAG(ARKWEB_PERFORMANCE_SCHEDULING)
  base::HighResolutionTimerManager hi_res_timer_manager;

  // Adds support of wall-time based TimerKeeper metrics for the main GPU thread
  // when command-line flag is set. CrGpuMain will be used as suffix for each
  // metric.
  if (command_line.HasSwitch(switches::kEnableGpuMainTimeKeeperMetrics)) {
    base::CurrentThread::Get()->EnableMessagePumpTimeKeeperMetrics(
        "CrGpuMain",
        /*wall_time_based_metrics_enabled_for_testing=*/true);
  }

  DCHECK(tracing::IsTracingInitialized());

  {
    TRACE_EVENT0("gpu", "Run Message Loop");
    run_loop.Run();
  }

  return dead_on_arrival ? RESULT_CODE_GPU_DEAD_ON_ARRIVAL : 0;
}

namespace {

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
bool StartSandboxLinux(gpu::GpuWatchdogThread* watchdog_thread,
                       const gpu::GPUInfo* gpu_info,
                       const gpu::GpuPreferences& gpu_prefs) {
  TRACE_EVENT("gpu,startup", "Initialize sandbox");

  if (watchdog_thread) {
    // SandboxLinux needs to be able to ensure that the thread
    // has really been stopped.
    sandbox::policy::SandboxLinux::GetInstance()->StopThread(watchdog_thread);
  }

  base::Thread* trace_thread =
      tracing::IsTracingInitialized()
          ? tracing::PerfettoTracedProcess::GetTraceThread()
          : nullptr;
  if (trace_thread) {
    sandbox::policy::SandboxLinux::GetInstance()->StopThread(trace_thread);
  }

  // SandboxLinux::InitializeSandbox() must always be called
  // with only one thread.
  sandbox::policy::SandboxLinux::Options sandbox_options;
  if (gpu_info) {
    // We have to enable sandbox settings for all GPUs in the system
    // for Chrome to be able to access/use them.
    sandbox_options.use_amd_specific_policies =
        angle::IsAMD(gpu_info->active_gpu().vendor_id);
    sandbox_options.use_intel_specific_policies =
        angle::IsIntel(gpu_info->active_gpu().vendor_id);
    sandbox_options.use_virtio_specific_policies =
        angle::IsVirtIO(gpu_info->active_gpu().vendor_id);
    sandbox_options.use_nvidia_specific_policies =
        angle::IsNVIDIA(gpu_info->active_gpu().vendor_id);
    for (const auto& gpu : gpu_info->secondary_gpus) {
      if (angle::IsAMD(gpu.vendor_id))
        sandbox_options.use_amd_specific_policies = true;
      else if (angle::IsIntel(gpu.vendor_id))
        sandbox_options.use_intel_specific_policies = true;
      else if (angle::IsNVIDIA(gpu.vendor_id))
        sandbox_options.use_nvidia_specific_policies = true;
    }
  }
  sandbox_options.accelerated_video_decode_enabled =
      !gpu_prefs.disable_accelerated_video_decode;
  sandbox_options.accelerated_video_encode_enabled =
      !gpu_prefs.disable_accelerated_video_encode;

#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
  // Video decoding of many video streams can use thousands of FDs as well as
  // Exo clients.
  // See https://crbug.com/1417237
  // With MappableSI the number of active GMBs has doubled.
  // See https://crbug.com/404365358
  const auto current_max_fds =
      base::saturated_cast<unsigned int>(base::GetMaxFds());
  constexpr unsigned int kMaxFDsDelta = 1u << 14;
  const auto new_max_fds =
      static_cast<unsigned int>(base::ClampMax(current_max_fds, kMaxFDsDelta));
  base::IncreaseFdLimitTo(new_max_fds);
#endif

  bool res = sandbox::policy::SandboxLinux::GetInstance()->InitializeSandbox(
      sandbox::policy::SandboxTypeFromCommandLine(
          *base::CommandLine::ForCurrentProcess()),
      base::BindOnce(GpuPreSandboxHook), sandbox_options);

  if (watchdog_thread) {
    watchdog_thread->Start();
  }

  if (trace_thread) {
    tracing::PerfettoTracedProcess::Get().RestartThreadInSandbox();
  }

  return res;
}
#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_ANDROID)
bool StartSandboxAndroid(gpu::GpuWatchdogThread* watchdog_thread) {
  if (watchdog_thread) {
    // Stop the watchdog thread temporarily.
    base::ScopedFD proc_fd(
        HANDLE_EINTR(open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC)));

    sandbox::ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.get(),
                                                     watchdog_thread);
  }

  bool res = sandbox::landlock::ApplyLandlock(
      sandbox::policy::SandboxTypeFromCommandLine(
          *base::CommandLine::ForCurrentProcess()));

  if (watchdog_thread) {
    watchdog_thread->Start();
  }

  return res;
}
#endif  // BUILDFLAG(IS_ANDROID)

#if BUILDFLAG(IS_WIN)
bool StartSandboxWindows(const sandbox::SandboxInterfaceInfo* sandbox_info) {
  TRACE_EVENT("gpu,startup", "Lower token");

  // Set up DirectReceiver before the sandbox is enabled.
  const bool should_init_transport =
      features::IsVizDirectCompositorThreadIpcNonRootEnabled() ||
      features::IsVizDirectCompositorThreadIpcFrameSinkManagerEnabled();
  if (should_init_transport) {
    // This pre-initializes a transport to be used for direct receiver since a
    // feature that will use it is enabled.
    mojo::CreateDirectReceiverTransportBeforeSandbox();
  }
  // For Windows, if the target_services interface is not zero, the process
  // is sandboxed and we must call LowerToken() before rendering untrusted
  // content.
  sandbox::TargetServices* target_services = sandbox_info->target_services;
  if (target_services) {
    target_services->LowerToken();
    return true;
  }

  return false;
}
#endif  // BUILDFLAG(IS_WIN)

#if BUILDFLAG(IS_OHOS)
bool StartSandboxOHOS(gpu::GpuWatchdogThread* watchdog_thread) {
  TRACE_EVENT0("gpu,startup", "Initialize sandbox");

  if (watchdog_thread) {
    // SandboxLinux needs to be able to ensure that the thread
    // has really been stopped.
    sandbox::policy::SandboxLinux::GetInstance()->StopThread(watchdog_thread);
  }

  // SandboxLinux::InitializeSandbox() must always be called
  // with only one thread.
  sandbox::policy::SandboxLinux::Options sandbox_options;

  bool res = sandbox::policy::SandboxLinux::GetInstance()->InitializeSandbox(
      sandbox::policy::SandboxTypeFromCommandLine(
          *base::CommandLine::ForCurrentProcess()),
      base::BindOnce(GpuPreSandboxHook), sandbox_options);

  if (watchdog_thread) {
    watchdog_thread->Start();
  }

  return res;
}
#endif

}  // namespace.

}  // namespace content