blob: d46d1cf9013ecb6cbda80c55582e3c9928f913bd [file] [log] [blame]
// Copyright 2017 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 "content/browser/child_process_launcher_helper_android.h"
#include <memory>
#include "base/android/apk_assets.h"
#include "base/android/context_utils.h"
#include "base/android/jni_array.h"
#include "base/android/unguessable_token_android.h"
#include "base/i18n/icu_util.h"
#include "base/logging.h"
#include "base/metrics/field_trial.h"
#include "content/browser/android/scoped_surface_request_manager.h"
#include "content/browser/child_process_launcher_helper.h"
#include "content/browser/child_process_launcher_helper_posix.h"
#include "content/browser/file_descriptor_info_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/content_descriptors.h"
#include "content/public/common/content_switches.h"
#include "gin/v8_initializer.h"
#include "gpu/ipc/common/gpu_surface_tracker.h"
#include "jni/ChildProcessLauncher_jni.h"
using base::android::AttachCurrentThread;
using base::android::JavaParamRef;
using base::android::ScopedJavaGlobalRef;
using base::android::ScopedJavaLocalRef;
using base::android::ToJavaArrayOfStrings;
namespace content {
typedef base::Callback<void(base::ProcessHandle, int /* launch result */)>
StartChildProcessCallback;
namespace internal {
namespace {
// Starts a process as a child process spawned by the Android ActivityManager.
// The created process handle is returned to the |callback| on success, 0 is
// returned if the process could not be created.
void StartChildProcess(const base::CommandLine::StringVector& argv,
int child_process_id,
content::FileDescriptorInfo* files_to_register,
const StartChildProcessCallback& callback) {
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
// Create the Command line String[]
ScopedJavaLocalRef<jobjectArray> j_argv = ToJavaArrayOfStrings(env, argv);
size_t file_count = files_to_register->GetMappingSize();
DCHECK(file_count > 0);
ScopedJavaLocalRef<jclass> j_file_info_class = base::android::GetClass(
env, "org/chromium/base/process_launcher/FileDescriptorInfo");
ScopedJavaLocalRef<jobjectArray> j_file_infos(
env, env->NewObjectArray(file_count, j_file_info_class.obj(), NULL));
base::android::CheckException(env);
for (size_t i = 0; i < file_count; ++i) {
int fd = files_to_register->GetFDAt(i);
PCHECK(0 <= fd);
int id = files_to_register->GetIDAt(i);
const auto& region = files_to_register->GetRegionAt(i);
bool auto_close = files_to_register->OwnsFD(fd);
ScopedJavaLocalRef<jobject> j_file_info =
Java_ChildProcessLauncher_makeFdInfo(env, id, fd, auto_close,
region.offset, region.size);
PCHECK(j_file_info.obj());
env->SetObjectArrayElement(j_file_infos.obj(), i, j_file_info.obj());
if (auto_close) {
ignore_result(files_to_register->ReleaseFD(fd).release());
}
}
constexpr int param_key = 0; // TODO(boliu): Use this.
Java_ChildProcessLauncher_start(
env, base::android::GetApplicationContext(), param_key, j_argv,
child_process_id, j_file_infos,
reinterpret_cast<intptr_t>(new StartChildProcessCallback(callback)));
}
// Stops a child process based on the handle returned from StartChildProcess.
void StopChildProcess(base::ProcessHandle handle) {
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
Java_ChildProcessLauncher_stop(env, static_cast<jint>(handle));
}
bool IsChildProcessOomProtected(base::ProcessHandle handle) {
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
return Java_ChildProcessLauncher_isOomProtected(env,
static_cast<jint>(handle));
}
void SetChildProcessInForeground(base::ProcessHandle handle,
bool in_foreground) {
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
return Java_ChildProcessLauncher_setInForeground(
env, static_cast<jint>(handle), static_cast<jboolean>(in_foreground));
}
// Callback invoked from Java once the process has been started.
void ChildProcessStartedCallback(
ChildProcessLauncherHelper* helper,
base::ProcessHandle handle,
int launch_result) {
// TODO(jcivelli): Remove this by defining better what happens on what thread
// in the corresponding Java code.
ChildProcessLauncherHelper::Process process;
process.process = base::Process(handle);
if (BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)) {
helper->PostLaunchOnLauncherThread(
std::move(process),
launch_result,
false); // post_launch_on_client_thread_called
return;
}
bool on_client_thread = BrowserThread::CurrentlyOn(
static_cast<BrowserThread::ID>(helper->client_thread_id()));
BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
base::Bind(&ChildProcessLauncherHelper::PostLaunchOnLauncherThread,
helper,
base::Passed(std::move(process)),
launch_result,
on_client_thread));
if (on_client_thread) {
ChildProcessLauncherHelper::Process process;
process.process = base::Process(handle);
helper->PostLaunchOnClientThread(std::move(process), launch_result);
}
}
} // namespace
void ChildProcessLauncherHelper::BeforeLaunchOnClientThread() {
// Android only supports renderer, sandboxed utility and gpu.
std::string process_type =
command_line()->GetSwitchValueASCII(switches::kProcessType);
CHECK(process_type == switches::kGpuProcess ||
process_type == switches::kRendererProcess ||
#if BUILDFLAG(ENABLE_PLUGINS)
process_type == switches::kPpapiPluginProcess ||
#endif
process_type == switches::kUtilityProcess)
<< "Unsupported process type: " << process_type;
// Non-sandboxed utility or renderer process are currently not supported.
DCHECK(process_type == switches::kGpuProcess ||
!command_line()->HasSwitch(switches::kNoSandbox));
}
mojo::edk::ScopedPlatformHandle
ChildProcessLauncherHelper::PrepareMojoPipeHandlesOnClientThread() {
return mojo::edk::ScopedPlatformHandle();
}
std::unique_ptr<FileDescriptorInfo>
ChildProcessLauncherHelper::GetFilesToMap() {
DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
// Android WebView runs in single process, ensure that we never get here when
// running in single process mode.
CHECK(!command_line()->HasSwitch(switches::kSingleProcess));
std::unique_ptr<FileDescriptorInfo> files_to_register =
CreateDefaultPosixFilesToMap(child_process_id(), mojo_client_handle(),
true /* include_service_required_files */,
GetProcessType(), command_line());
#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
base::MemoryMappedFile::Region icu_region;
int fd = base::i18n::GetIcuDataFileHandle(&icu_region);
files_to_register->ShareWithRegion(kAndroidICUDataDescriptor, fd, icu_region);
#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
return files_to_register;
}
void ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
const FileDescriptorInfo& files_to_register,
base::LaunchOptions* options) {
}
ChildProcessLauncherHelper::Process
ChildProcessLauncherHelper::LaunchProcessOnLauncherThread(
const base::LaunchOptions& options,
std::unique_ptr<FileDescriptorInfo> files_to_register,
bool* is_synchronous_launch,
int* launch_result) {
*is_synchronous_launch = false;
StartChildProcess(command_line()->argv(),
child_process_id(),
files_to_register.get(),
base::Bind(&ChildProcessStartedCallback,
RetainedRef(this)));
return Process();
}
void ChildProcessLauncherHelper::AfterLaunchOnLauncherThread(
const ChildProcessLauncherHelper::Process& process,
const base::LaunchOptions& options) {
}
// static
base::TerminationStatus ChildProcessLauncherHelper::GetTerminationStatus(
const ChildProcessLauncherHelper::Process& process,
bool known_dead,
int* exit_code) {
if (IsChildProcessOomProtected(process.process.Handle()))
return base::TERMINATION_STATUS_OOM_PROTECTED;
return base::GetTerminationStatus(process.process.Handle(), exit_code);
}
// static
bool ChildProcessLauncherHelper::TerminateProcess(
const base::Process& process, int exit_code, bool wait) {
StopChildProcess(process.Handle());
return true;
}
// static
void ChildProcessLauncherHelper::ForceNormalProcessTerminationSync(
ChildProcessLauncherHelper::Process process) {
DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
VLOG(1) << "ChromeProcess: Stopping process with handle "
<< process.process.Handle();
StopChildProcess(process.process.Handle());
}
// static
void ChildProcessLauncherHelper::SetProcessBackgroundedOnLauncherThread(
base::Process process, bool background) {
SetChildProcessInForeground(process.Handle(), !background);
}
// static
void ChildProcessLauncherHelper::SetRegisteredFilesForService(
const std::string& service_name,
catalog::RequiredFileMap required_files) {
SetFilesToShareForServicePosix(service_name, std::move(required_files));
}
// static
void ChildProcessLauncherHelper::ResetRegisteredFilesForTesting() {
ResetFilesToShareForTestingPosix();
}
// static
base::File OpenFileToShare(const base::FilePath& path,
base::MemoryMappedFile::Region* region) {
return base::File(base::android::OpenApkAsset(path.value(), region));
}
} // namespace internal
// Called from ChildProcessLauncher.java when the ChildProcess was
// started.
// |client_context| is the pointer to StartChildProcessCallback which was
// passed in from StartChildProcess.
// |handle| is the processID of the child process as originated in Java, 0 if
// the ChildProcess could not be created.
static void OnChildProcessStarted(JNIEnv*,
const JavaParamRef<jclass>&,
jlong client_context,
jint handle) {
StartChildProcessCallback* callback =
reinterpret_cast<StartChildProcessCallback*>(client_context);
int launch_result = (handle == base::kNullProcessHandle)
? LAUNCH_RESULT_FAILURE
: LAUNCH_RESULT_SUCCESS;
callback->Run(static_cast<base::ProcessHandle>(handle), launch_result);
delete callback;
}
void CompleteScopedSurfaceRequest(JNIEnv* env,
const JavaParamRef<jclass>& clazz,
const JavaParamRef<jobject>& token,
const JavaParamRef<jobject>& surface) {
base::UnguessableToken requestToken =
base::android::UnguessableTokenAndroid::FromJavaUnguessableToken(env,
token);
if (!requestToken) {
DLOG(ERROR) << "Received invalid surface request token.";
return;
}
DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
ScopedJavaGlobalRef<jobject> jsurface;
jsurface.Reset(env, surface);
ScopedSurfaceRequestManager::GetInstance()->FulfillScopedSurfaceRequest(
requestToken, gl::ScopedJavaSurface(jsurface));
}
jboolean IsSingleProcess(JNIEnv* env, const JavaParamRef<jclass>& clazz) {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kSingleProcess);
}
base::android::ScopedJavaLocalRef<jobject> GetViewSurface(
JNIEnv* env,
const base::android::JavaParamRef<jclass>& jcaller,
jint surface_id) {
gl::ScopedJavaSurface surface_view =
gpu::GpuSurfaceTracker::GetInstance()->AcquireJavaSurface(surface_id);
return base::android::ScopedJavaLocalRef<jobject>(surface_view.j_surface());
}
bool RegisterChildProcessLauncher(JNIEnv* env) {
return RegisterNativesImpl(env);
}
} // namespace content