blob: 511b0596ff7d0513f98b24e6c91d8361df932a8d [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.
package org.chromium.content.app;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.SparseArray;
import android.view.Surface;
import org.chromium.base.CommandLine;
import org.chromium.base.JNIUtils;
import org.chromium.base.Log;
import org.chromium.base.UnguessableToken;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.MainDex;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.library_loader.Linker;
import org.chromium.base.library_loader.ProcessInitException;
import org.chromium.base.memory.MemoryPressureUma;
import org.chromium.base.process_launcher.ChildProcessServiceDelegate;
import org.chromium.base.task.PostTask;
import org.chromium.content.browser.ChildProcessCreationParamsImpl;
import org.chromium.content.browser.ContentChildProcessConstants;
import org.chromium.content.common.IGpuProcessCallback;
import org.chromium.content.common.SurfaceWrapper;
import org.chromium.content_public.browser.UiThreadTaskTraits;
import org.chromium.content_public.common.ContentProcessInfo;
import org.chromium.content_public.common.ContentSwitches;
import java.util.List;
/**
* This implementation of {@link ChildProcessServiceDelegate} loads the native library potentially
* using the custom linker, provides access to view surfaces.
*/
@JNINamespace("content")
@MainDex
public class ContentChildProcessServiceDelegate implements ChildProcessServiceDelegate {
private static final String TAG = "ContentCPSDelegate";
// Linker-specific parameters for this child process service.
private ChromiumLinkerParams mLinkerParams;
// Child library process type.
private int mLibraryProcessType;
private IGpuProcessCallback mGpuCallback;
private int mCpuCount;
private long mCpuFeatures;
private SparseArray<String> mFdsIdsToKeys;
public ContentChildProcessServiceDelegate() {
KillChildUncaughtExceptionHandler.maybeInstallHandler();
}
@Override
public void onServiceCreated() {
ContentProcessInfo.setInChildProcess(true);
}
@Override
public void onServiceBound(Intent intent) {
mLinkerParams = ChromiumLinkerParams.create(intent.getExtras());
mLibraryProcessType =
ChildProcessCreationParamsImpl.getLibraryProcessType(intent.getExtras());
}
@Override
public void onConnectionSetup(Bundle connectionBundle, List<IBinder> clientInterfaces) {
mGpuCallback = clientInterfaces != null && !clientInterfaces.isEmpty()
? IGpuProcessCallback.Stub.asInterface(clientInterfaces.get(0))
: null;
mCpuCount = connectionBundle.getInt(ContentChildProcessConstants.EXTRA_CPU_COUNT);
mCpuFeatures = connectionBundle.getLong(ContentChildProcessConstants.EXTRA_CPU_FEATURES);
assert mCpuCount > 0;
if (LibraryLoader.useCrazyLinker()) {
Bundle sharedRelros = connectionBundle.getBundle(Linker.EXTRA_LINKER_SHARED_RELROS);
if (sharedRelros != null) {
getLinker().useSharedRelros(sharedRelros);
}
}
}
@Override
public void preloadNativeLibrary(Context hostContext) {
// This function can be called before command line is set. That is fine because
// preloading explicitly doesn't run any Chromium code, see NativeLibraryPreloader
// for more info.
LibraryLoader.getInstance().preloadNowOverrideApplicationContext(hostContext);
}
@Override
public boolean loadNativeLibrary(Context hostContext) {
String processType =
CommandLine.getInstance().getSwitchValue(ContentSwitches.SWITCH_PROCESS_TYPE);
// Enable selective JNI registration when the process is not the browser process.
if (processType != null) {
JNIUtils.enableSelectiveJniRegistration();
}
Linker linker = null;
boolean requestedSharedRelro = false;
if (LibraryLoader.useCrazyLinker()) {
assert mLinkerParams != null;
linker = getLinker();
if (mLinkerParams.mWaitForSharedRelro) {
requestedSharedRelro = true;
linker.initServiceProcess(mLinkerParams.mBaseLoadAddress);
} else {
linker.disableSharedRelros();
}
}
boolean isLoaded = false;
boolean loadAtFixedAddressFailed = false;
try {
LibraryLoader.getInstance().loadNowOverrideApplicationContext(hostContext);
isLoaded = true;
} catch (ProcessInitException e) {
if (requestedSharedRelro) {
Log.w(TAG,
"Failed to load native library with shared RELRO, "
+ "retrying without");
loadAtFixedAddressFailed = true;
} else {
Log.e(TAG, "Failed to load native library", e);
}
}
if (!isLoaded && requestedSharedRelro) {
linker.disableSharedRelros();
try {
LibraryLoader.getInstance().loadNowOverrideApplicationContext(hostContext);
isLoaded = true;
} catch (ProcessInitException e) {
Log.e(TAG, "Failed to load native library on retry", e);
}
}
if (!isLoaded) {
return false;
}
LibraryLoader.getInstance().registerRendererProcessHistogram(
requestedSharedRelro, loadAtFixedAddressFailed);
try {
LibraryLoader.getInstance().initialize(mLibraryProcessType);
} catch (ProcessInitException e) {
Log.w(TAG, "startup failed: %s", e);
return false;
}
// Now that the library is loaded, get the FD map,
// TODO(jcivelli): can this be done in onBeforeMain? We would have to mode onBeforeMain
// so it's called before FDs are registered.
nativeRetrieveFileDescriptorsIdsToKeys();
return true;
}
@Override
public SparseArray<String> getFileDescriptorsIdsToKeys() {
assert mFdsIdsToKeys != null;
return mFdsIdsToKeys;
}
@Override
public void onBeforeMain() {
nativeInitChildProcess(mCpuCount, mCpuFeatures);
PostTask.postTask(
UiThreadTaskTraits.DEFAULT, () -> MemoryPressureUma.initializeForChildService());
}
@Override
public void onDestroy() {
// Try to shutdown the MainThread gracefully, but it might not have a
// chance to exit normally.
nativeShutdownMainThread();
}
@Override
public void runMain() {
ContentMain.start(false);
}
// Return a Linker instance. If testing, the Linker needs special setup.
private Linker getLinker() {
if (Linker.areTestsEnabled()) {
// For testing, set the Linker implementation and the test runner
// class name to match those used by the parent.
assert mLinkerParams != null;
Linker.setupForTesting(mLinkerParams.mTestRunnerClassNameForTesting);
}
return Linker.getInstance();
}
@CalledByNative
private void setFileDescriptorsIdsToKeys(int[] ids, String[] keys) {
assert ids.length == keys.length;
assert mFdsIdsToKeys == null;
mFdsIdsToKeys = new SparseArray<>();
for (int i = 0; i < ids.length; ++i) {
mFdsIdsToKeys.put(ids[i], keys[i]);
}
}
@SuppressWarnings("unused")
@CalledByNative
private void forwardSurfaceForSurfaceRequest(UnguessableToken requestToken, Surface surface) {
if (mGpuCallback == null) {
Log.e(TAG, "No callback interface has been provided.");
return;
}
try {
mGpuCallback.forwardSurfaceForSurfaceRequest(requestToken, surface);
} catch (RemoteException e) {
Log.e(TAG, "Unable to call forwardSurfaceForSurfaceRequest: %s", e);
return;
} finally {
surface.release();
}
}
@SuppressWarnings("unused")
@CalledByNative
private Surface getViewSurface(int surfaceId) {
if (mGpuCallback == null) {
Log.e(TAG, "No callback interface has been provided.");
return null;
}
try {
SurfaceWrapper wrapper = mGpuCallback.getViewSurface(surfaceId);
return wrapper != null ? wrapper.getSurface() : null;
} catch (RemoteException e) {
Log.e(TAG, "Unable to call getViewSurface: %s", e);
return null;
}
}
/**
* Initializes the native parts of the service.
*
* @param serviceImpl This ChildProcessService object.
* @param cpuCount The number of CPUs.
* @param cpuFeatures The CPU features.
*/
private native void nativeInitChildProcess(int cpuCount, long cpuFeatures);
private native void nativeShutdownMainThread();
// Retrieves the FD IDs to keys map and set it by calling setFileDescriptorsIdsToKeys().
private native void nativeRetrieveFileDescriptorsIdsToKeys();
}