This source file includes following definitions.
- createServiceBindIntent
- bind
- unbind
- isBound
- onServiceConnected
- onServiceDisconnected
- getServiceNumber
- isInSandbox
- getService
- getPid
- start
- setupConnection
- stop
- onBindFailed
- doConnectionSetup
- isInitialBindingBound
- isStrongBindingBound
- removeInitialBinding
- isOomProtectedOrWasWhenDied
- dropOomBindings
- addStrongBinding
- removeStrongBinding
package org.chromium.content.browser;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import org.chromium.base.CpuFeatures;
import org.chromium.base.ThreadUtils;
import org.chromium.base.TraceEvent;
import org.chromium.base.library_loader.Linker;
import org.chromium.content.app.ChildProcessService;
import org.chromium.content.app.ChromiumLinkerParams;
import org.chromium.content.common.IChildProcessCallback;
import org.chromium.content.common.IChildProcessService;
import java.io.IOException;
public class ChildProcessConnectionImpl implements ChildProcessConnection {
private final Context mContext;
private final int mServiceNumber;
private final boolean mInSandbox;
private final ChildProcessConnection.DeathCallback mDeathCallback;
private final Class<? extends ChildProcessService> mServiceClass;
private final Object mLock = new Object();
private IChildProcessService mService = null;
private boolean mServiceConnectComplete = false;
private boolean mServiceDisconnected = false;
private boolean mWasOomProtected = false;
private int mPID = 0;
private ChildServiceConnection mInitialBinding = null;
private ChildServiceConnection mStrongBinding = null;
private ChildServiceConnection mWaivedBinding = null;
private int mStrongBindingCount = 0;
private ChromiumLinkerParams mLinkerParams = null;
private static final String TAG = "ChildProcessConnection";
private static class ConnectionParams {
final String[] mCommandLine;
final FileDescriptorInfo[] mFilesToBeMapped;
final IChildProcessCallback mCallback;
final Bundle mSharedRelros;
ConnectionParams(String[] commandLine, FileDescriptorInfo[] filesToBeMapped,
IChildProcessCallback callback, Bundle sharedRelros) {
mCommandLine = commandLine;
mFilesToBeMapped = filesToBeMapped;
mCallback = callback;
mSharedRelros = sharedRelros;
}
}
private ConnectionParams mConnectionParams;
private ChildProcessConnection.ConnectionCallback mConnectionCallback;
private class ChildServiceConnection implements ServiceConnection {
private boolean mBound = false;
private final int mBindFlags;
private Intent createServiceBindIntent() {
Intent intent = new Intent();
intent.setClassName(mContext, mServiceClass.getName() + mServiceNumber);
intent.setPackage(mContext.getPackageName());
return intent;
}
public ChildServiceConnection(int bindFlags) {
mBindFlags = bindFlags;
}
boolean bind(String[] commandLine) {
if (!mBound) {
final Intent intent = createServiceBindIntent();
if (commandLine != null) {
intent.putExtra(EXTRA_COMMAND_LINE, commandLine);
}
if (mLinkerParams != null)
mLinkerParams.addIntentExtras(intent);
mBound = mContext.bindService(intent, this, mBindFlags);
}
return mBound;
}
void unbind() {
if (mBound) {
mContext.unbindService(this);
mBound = false;
}
}
boolean isBound() {
return mBound;
}
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
synchronized (mLock) {
if (mServiceConnectComplete) {
return;
}
TraceEvent.begin();
mServiceConnectComplete = true;
mService = IChildProcessService.Stub.asInterface(service);
if (mConnectionParams != null) {
doConnectionSetup();
}
TraceEvent.end();
}
}
@Override
public void onServiceDisconnected(ComponentName className) {
synchronized (mLock) {
if (mServiceDisconnected) {
return;
}
mServiceDisconnected = true;
mWasOomProtected = mInitialBinding.isBound() || mStrongBinding.isBound();
}
int pid = mPID;
boolean disconnectedWhileBeingSetUp = mConnectionParams != null;
Log.w(TAG, "onServiceDisconnected (crash or killed by oom): pid=" + pid);
stop();
if (pid != 0) {
mDeathCallback.onChildProcessDied(pid);
}
if (disconnectedWhileBeingSetUp && mConnectionCallback != null) {
mConnectionCallback.onConnected(0);
}
}
}
ChildProcessConnectionImpl(Context context, int number, boolean inSandbox,
ChildProcessConnection.DeathCallback deathCallback,
Class<? extends ChildProcessService> serviceClass,
ChromiumLinkerParams chromiumLinkerParams) {
mContext = context;
mServiceNumber = number;
mInSandbox = inSandbox;
mDeathCallback = deathCallback;
mServiceClass = serviceClass;
mLinkerParams = chromiumLinkerParams;
mInitialBinding = new ChildServiceConnection(Context.BIND_AUTO_CREATE);
mStrongBinding = new ChildServiceConnection(
Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);
mWaivedBinding = new ChildServiceConnection(
Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY);
}
@Override
public int getServiceNumber() {
return mServiceNumber;
}
@Override
public boolean isInSandbox() {
return mInSandbox;
}
@Override
public IChildProcessService getService() {
synchronized (mLock) {
return mService;
}
}
@Override
public int getPid() {
synchronized (mLock) {
return mPID;
}
}
@Override
public void start(String[] commandLine) {
synchronized (mLock) {
TraceEvent.begin();
assert !ThreadUtils.runningOnUiThread();
if (!mInitialBinding.bind(commandLine)) {
onBindFailed();
} else {
mWaivedBinding.bind(null);
}
TraceEvent.end();
}
}
@Override
public void setupConnection(
String[] commandLine,
FileDescriptorInfo[] filesToBeMapped,
IChildProcessCallback processCallback,
ConnectionCallback connectionCallbacks,
Bundle sharedRelros) {
synchronized (mLock) {
TraceEvent.begin();
assert mConnectionParams == null;
mConnectionCallback = connectionCallbacks;
mConnectionParams = new ConnectionParams(
commandLine, filesToBeMapped, processCallback, sharedRelros);
if (mServiceConnectComplete) {
doConnectionSetup();
}
TraceEvent.end();
}
}
@Override
public void stop() {
synchronized (mLock) {
mInitialBinding.unbind();
mStrongBinding.unbind();
mWaivedBinding.unbind();
mStrongBindingCount = 0;
if (mService != null) {
mService = null;
mPID = 0;
}
mConnectionParams = null;
mServiceConnectComplete = false;
}
}
private void onBindFailed() {
mServiceConnectComplete = true;
if (mConnectionParams != null) {
doConnectionSetup();
}
}
private void doConnectionSetup() {
TraceEvent.begin();
assert mServiceConnectComplete && mConnectionParams != null;
if (mService != null) {
Bundle bundle = new Bundle();
bundle.putStringArray(EXTRA_COMMAND_LINE, mConnectionParams.mCommandLine);
FileDescriptorInfo[] fileInfos = mConnectionParams.mFilesToBeMapped;
ParcelFileDescriptor[] parcelFiles = new ParcelFileDescriptor[fileInfos.length];
for (int i = 0; i < fileInfos.length; i++) {
if (fileInfos[i].mFd == -1) {
Log.e(TAG, "Invalid FD (id=" + fileInfos[i].mId + ") for process connection, "
+ "aborting connection.");
return;
}
String idName = EXTRA_FILES_PREFIX + i + EXTRA_FILES_ID_SUFFIX;
String fdName = EXTRA_FILES_PREFIX + i + EXTRA_FILES_FD_SUFFIX;
if (fileInfos[i].mAutoClose) {
parcelFiles[i] = ParcelFileDescriptor.adoptFd(fileInfos[i].mFd);
} else {
try {
parcelFiles[i] = ParcelFileDescriptor.fromFd(fileInfos[i].mFd);
} catch (IOException e) {
Log.e(TAG,
"Invalid FD provided for process connection, aborting connection.",
e);
return;
}
}
bundle.putParcelable(fdName, parcelFiles[i]);
bundle.putInt(idName, fileInfos[i].mId);
}
bundle.putInt(EXTRA_CPU_COUNT, CpuFeatures.getCount());
bundle.putLong(EXTRA_CPU_FEATURES, CpuFeatures.getMask());
bundle.putBundle(Linker.EXTRA_LINKER_SHARED_RELROS,
mConnectionParams.mSharedRelros);
try {
mPID = mService.setupConnection(bundle, mConnectionParams.mCallback);
} catch (android.os.RemoteException re) {
Log.e(TAG, "Failed to setup connection.", re);
}
try {
for (ParcelFileDescriptor parcelFile : parcelFiles) {
if (parcelFile != null) parcelFile.close();
}
} catch (IOException ioe) {
Log.w(TAG, "Failed to close FD.", ioe);
}
}
mConnectionParams = null;
if (mConnectionCallback != null) {
mConnectionCallback.onConnected(getPid());
}
TraceEvent.end();
}
@Override
public boolean isInitialBindingBound() {
synchronized (mLock) {
return mInitialBinding.isBound();
}
}
@Override
public boolean isStrongBindingBound() {
synchronized (mLock) {
return mStrongBinding.isBound();
}
}
@Override
public void removeInitialBinding() {
synchronized (mLock) {
mInitialBinding.unbind();
}
}
@Override
public boolean isOomProtectedOrWasWhenDied() {
synchronized (mLock) {
if (mServiceDisconnected) {
return mWasOomProtected;
} else {
return mInitialBinding.isBound() || mStrongBinding.isBound();
}
}
}
@Override
public void dropOomBindings() {
synchronized (mLock) {
mInitialBinding.unbind();
mStrongBindingCount = 0;
mStrongBinding.unbind();
}
}
@Override
public void addStrongBinding() {
synchronized (mLock) {
if (mService == null) {
Log.w(TAG, "The connection is not bound for " + mPID);
return;
}
if (mStrongBindingCount == 0) {
mStrongBinding.bind(null);
}
mStrongBindingCount++;
}
}
@Override
public void removeStrongBinding() {
synchronized (mLock) {
if (mService == null) {
Log.w(TAG, "The connection is not bound for " + mPID);
return;
}
assert mStrongBindingCount > 0;
mStrongBindingCount--;
if (mStrongBindingCount == 0) {
mStrongBinding.unbind();
}
}
}
}