| Index: content/public/android/java/src/org/chromium/content/browser/BaseChildProcessConnection.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java b/content/public/android/java/src/org/chromium/content/browser/BaseChildProcessConnection.java
|
| similarity index 61%
|
| rename from content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java
|
| rename to content/public/android/java/src/org/chromium/content/browser/BaseChildProcessConnection.java
|
| index 710741b69c86a045512bf88f0459e6b9fd7934e7..52bfe5e5a7876a9868afff7493a07b436c6e7a19 100644
|
| --- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/BaseChildProcessConnection.java
|
| @@ -26,97 +26,72 @@ import org.chromium.base.process_launcher.IChildProcessService;
|
| import java.io.IOException;
|
|
|
| import javax.annotation.Nullable;
|
| +import javax.annotation.concurrent.GuardedBy;
|
|
|
| /**
|
| * Manages a connection between the browser activity and a child service.
|
| */
|
| -public class ChildProcessConnectionImpl implements ChildProcessConnection {
|
| - private final Context mContext;
|
| - private final int mServiceNumber;
|
| - private final boolean mInSandbox;
|
| - private final ChildProcessConnection.DeathCallback mDeathCallback;
|
| - private final ComponentName mServiceName;
|
| -
|
| - // Synchronization: While most internal flow occurs on the UI thread, the public API
|
| - // (specifically start and stop) may be called from any thread, hence all entry point methods
|
| - // into the class are synchronized on the lock to protect access to these members.
|
| - private final Object mLock = new Object();
|
| - private IChildProcessService mService;
|
| - // Set to true when the service connection callback runs. This differs from
|
| - // mServiceConnectComplete, which tracks that the connection completed successfully.
|
| - private boolean mDidOnServiceConnected;
|
| - // Set to true when the service connected successfully.
|
| - private boolean mServiceConnectComplete;
|
| - // Set to true when the service disconnects, as opposed to being properly closed. This happens
|
| - // when the process crashes or gets killed by the system out-of-memory killer.
|
| - private boolean mServiceDisconnected;
|
| - // When the service disconnects (i.e. mServiceDisconnected is set to true), the status of the
|
| - // oom bindings is stashed here for future inspection.
|
| - private boolean mWasOomProtected;
|
| - private int mPid; // Process ID of the corresponding child process.
|
| - // Initial binding protects the newly spawned process from being killed before it is put to use,
|
| - // it is maintained between calls to start() and removeInitialBinding().
|
| - private ChildServiceConnection mInitialBinding;
|
| - // Strong binding will make the service priority equal to the priority of the activity. We want
|
| - // the OS to be able to kill background renderers as it kills other background apps, so strong
|
| - // bindings are maintained only for services that are active at the moment (between
|
| - // addStrongBinding() and removeStrongBinding()).
|
| - private ChildServiceConnection mStrongBinding;
|
| - // Low priority binding maintained in the entire lifetime of the connection, i.e. between calls
|
| - // to start() and stop().
|
| - private ChildServiceConnection mWaivedBinding;
|
| - // Incremented on addStrongBinding(), decremented on removeStrongBinding().
|
| - private int mStrongBindingCount;
|
| - // Moderate binding will make the service priority equal to the priority of a visible process
|
| - // while the app is in the foreground. It will stay bound only while the app is in the
|
| - // foreground to protect a background process from the system out-of-memory killer.
|
| - private ChildServiceConnection mModerateBinding;
|
| -
|
| - // Parameters passed to the child process through the service binding intent.
|
| - // If the service gets recreated by the framework the intent will be reused, so these parameters
|
| - // should be common to all processes of that type.
|
| - private final Bundle mChildProcessCommonParameters;
|
| -
|
| - private final boolean mAlwaysInForeground;
|
| - private final ChildProcessCreationParams mCreationParams;
|
| +public abstract class BaseChildProcessConnection {
|
| + private static final String TAG = "BaseChildProcessConn";
|
|
|
| - // Caches whether non-sandboxed and sandboxed services require an extra
|
| - // binding flag provided via ChildProcessCreationParams.
|
| - // TODO(mnaganov): Get rid of it after the release of the next Android SDK.
|
| - private static Boolean sNeedsExtrabindFlags[] = new Boolean[2];
|
| -
|
| - private static final String TAG = "ChildProcessConnect";
|
| -
|
| - private static class ConnectionParams {
|
| - final String[] mCommandLine;
|
| - final FileDescriptorInfo[] mFilesToBeMapped;
|
| - final IBinder mCallback;
|
| -
|
| - ConnectionParams(
|
| - String[] commandLine, FileDescriptorInfo[] filesToBeMapped, IBinder callback) {
|
| - mCommandLine = commandLine;
|
| - mFilesToBeMapped = filesToBeMapped;
|
| - mCallback = callback;
|
| - }
|
| + /**
|
| + * Used to notify the consumer about disconnection of the service. This callback is provided
|
| + * earlier than ConnectionCallbacks below, as a child process might die before the connection is
|
| + * fully set up.
|
| + */
|
| + interface DeathCallback {
|
| + // Called on Launcher thread.
|
| + void onChildProcessDied(BaseChildProcessConnection connection);
|
| }
|
|
|
| - // This is set in start() and is used in onServiceConnected().
|
| - private ChildProcessConnection.StartCallback mStartCallback;
|
| + /**
|
| + * Used to notify the consumer about the process start. These callbacks will be invoked before
|
| + * the ConnectionCallbacks.
|
| + */
|
| + interface StartCallback {
|
| + /**
|
| + * Called when the child process has successfully started and is ready for connection
|
| + * setup.
|
| + */
|
| + void onChildStarted();
|
| +
|
| + /**
|
| + * Called when the child process failed to start. This can happen if the process is already
|
| + * in use by another client.
|
| + */
|
| + void onChildStartFailed();
|
| + }
|
|
|
| - // This is set in setupConnection() and is later used in doConnectionSetupLocked(), after which
|
| - // the variable is cleared. Therefore this is only valid while the connection is being set up.
|
| - private ConnectionParams mConnectionParams;
|
| + /**
|
| + * Used to notify the consumer about the connection being established.
|
| + */
|
| + interface ConnectionCallback {
|
| + /**
|
| + * Called when the connection to the service is established.
|
| + * @param connecion the connection object to the child process
|
| + */
|
| + void onConnected(BaseChildProcessConnection connection);
|
| + }
|
|
|
| - // Callback provided in setupConnection() that will communicate the result to the caller. This
|
| - // has to be called exactly once after setupConnection(), even if setup fails, so that the
|
| - // caller can free up resources associated with the setup attempt. This is set to null after the
|
| - // call.
|
| - private ChildProcessConnection.ConnectionCallback mConnectionCallback;
|
| + /** Used to create specialization connection instances. */
|
| + interface Factory {
|
| + BaseChildProcessConnection create(Context context, int number, boolean sandboxed,
|
| + DeathCallback deathCallback, String serviceClassName,
|
| + Bundle childProcessCommonParameters, ChildProcessCreationParams creationParams);
|
| + }
|
|
|
| - private class ChildServiceConnection implements ServiceConnection {
|
| - private boolean mBound;
|
| + /** Interface representing a connection to the Android service. Can be mocked in unit-tests. */
|
| + protected interface ChildServiceConnection {
|
| + boolean bind();
|
| + void unbind();
|
| + boolean isBound();
|
| + }
|
|
|
| + /** Implementation of ChildServiceConnection that does connect to a service. */
|
| + protected class ChildServiceConnectionImpl
|
| + implements ChildServiceConnection, ServiceConnection {
|
| private final int mBindFlags;
|
| + private boolean mBound;
|
|
|
| private Intent createServiceBindIntent() {
|
| Intent intent = new Intent();
|
| @@ -127,34 +102,37 @@ public class ChildProcessConnectionImpl implements ChildProcessConnection {
|
| return intent;
|
| }
|
|
|
| - public ChildServiceConnection(int bindFlags) {
|
| + private ChildServiceConnectionImpl(int bindFlags) {
|
| mBindFlags = bindFlags;
|
| }
|
|
|
| - boolean bind() {
|
| + @Override
|
| + public boolean bind() {
|
| if (!mBound) {
|
| try {
|
| - TraceEvent.begin("ChildProcessConnectionImpl.ChildServiceConnection.bind");
|
| + TraceEvent.begin("BaseChildProcessConnection.ChildServiceConnection.bind");
|
| Intent intent = createServiceBindIntent();
|
| if (mChildProcessCommonParameters != null) {
|
| intent.putExtras(mChildProcessCommonParameters);
|
| }
|
| mBound = mContext.bindService(intent, this, mBindFlags);
|
| } finally {
|
| - TraceEvent.end("ChildProcessConnectionImpl.ChildServiceConnection.bind");
|
| + TraceEvent.end("BaseChildProcessConnection.ChildServiceConnection.bind");
|
| }
|
| }
|
| return mBound;
|
| }
|
|
|
| - void unbind() {
|
| + @Override
|
| + public void unbind() {
|
| if (mBound) {
|
| mContext.unbindService(this);
|
| mBound = false;
|
| }
|
| }
|
|
|
| - boolean isBound() {
|
| + @Override
|
| + public boolean isBound() {
|
| return mBound;
|
| }
|
|
|
| @@ -163,7 +141,7 @@ public class ChildProcessConnectionImpl implements ChildProcessConnection {
|
| LauncherThread.post(new Runnable() {
|
| @Override
|
| public void run() {
|
| - ChildProcessConnectionImpl.this.onServiceConnectedOnLauncherThread(service);
|
| + BaseChildProcessConnection.this.onServiceConnectedOnLauncherThread(service);
|
| }
|
| });
|
| }
|
| @@ -174,123 +152,178 @@ public class ChildProcessConnectionImpl implements ChildProcessConnection {
|
| LauncherThread.post(new Runnable() {
|
| @Override
|
| public void run() {
|
| - ChildProcessConnectionImpl.this.onServiceDisconnectedOnLauncherThread();
|
| + BaseChildProcessConnection.this.onServiceDisconnectedOnLauncherThread();
|
| }
|
| });
|
| }
|
| }
|
|
|
| - ChildProcessConnectionImpl(Context context, int number, boolean inSandbox,
|
| - ChildProcessConnection.DeathCallback deathCallback, String serviceClassName,
|
| - Bundle childProcessCommonParameters, boolean alwaysInForeground,
|
| - ChildProcessCreationParams creationParams) {
|
| + // Caches whether non-sandboxed and sandboxed services require an extra
|
| + // binding flag provided via ChildProcessCreationParams.
|
| + // TODO(mnaganov): Get rid of it after the release of the next Android SDK.
|
| + private static Boolean sNeedsExtrabindFlags[] = new Boolean[2];
|
| + private final Context mContext;
|
| + private final int mServiceNumber;
|
| + private final boolean mSandboxed;
|
| + private final BaseChildProcessConnection.DeathCallback mDeathCallback;
|
| + private final ComponentName mServiceName;
|
| +
|
| + // Parameters passed to the child process through the service binding intent.
|
| + // If the service gets recreated by the framework the intent will be reused, so these parameters
|
| + // should be common to all processes of that type.
|
| + private final Bundle mChildProcessCommonParameters;
|
| +
|
| + private final ChildProcessCreationParams mCreationParams;
|
| +
|
| + private static class ConnectionParams {
|
| + final String[] mCommandLine;
|
| + final FileDescriptorInfo[] mFilesToBeMapped;
|
| + final IBinder mCallback;
|
| +
|
| + ConnectionParams(
|
| + String[] commandLine, FileDescriptorInfo[] filesToBeMapped, IBinder callback) {
|
| + mCommandLine = commandLine;
|
| + mFilesToBeMapped = filesToBeMapped;
|
| + mCallback = callback;
|
| + }
|
| + }
|
| +
|
| + // Synchronization: While most internal flow occurs on the UI thread, the public API
|
| + // (specifically start and stop) may be called from any thread, hence all entry point methods
|
| + // into the class are synchronized on the lock to protect access to these members.
|
| + // TODO(jcivelli): crbug.com/714657 remove this lock.
|
| + private final Object mLock = new Object();
|
| +
|
| + // This is set in start() and is used in onServiceConnected().
|
| + @GuardedBy("mLock")
|
| + private StartCallback mStartCallback;
|
| +
|
| + // This is set in setupConnection() and is later used in doConnectionSetupLocked(), after which
|
| + // the variable is cleared. Therefore this is only valid while the connection is being set up.
|
| + @GuardedBy("mLock")
|
| + private ConnectionParams mConnectionParams;
|
| +
|
| + // Callback provided in setupConnection() that will communicate the result to the caller. This
|
| + // has to be called exactly once after setupConnection(), even if setup fails, so that the
|
| + // caller can free up resources associated with the setup attempt. This is set to null after the
|
| + // call.
|
| + @GuardedBy("mLock")
|
| + private ConnectionCallback mConnectionCallback;
|
| +
|
| + @GuardedBy("mLock")
|
| + private IChildProcessService mService;
|
| +
|
| + // Set to true when the service connection callback runs. This differs from
|
| + // mServiceConnectComplete, which tracks that the connection completed successfully.
|
| + @GuardedBy("mLock")
|
| + private boolean mDidOnServiceConnected;
|
| +
|
| + // Set to true when the service connected successfully.
|
| + @GuardedBy("mLock")
|
| + private boolean mServiceConnectComplete;
|
| +
|
| + // Set to true when the service disconnects, as opposed to being properly closed. This happens
|
| + // when the process crashes or gets killed by the system out-of-memory killer.
|
| + @GuardedBy("mLock")
|
| + private boolean mServiceDisconnected;
|
| +
|
| + // Process ID of the corresponding child process.
|
| + @GuardedBy("mLock")
|
| + private int mPid;
|
| +
|
| + protected BaseChildProcessConnection(Context context, int number, boolean sandboxed,
|
| + DeathCallback deathCallback, String serviceClassName,
|
| + Bundle childProcessCommonParameters, ChildProcessCreationParams creationParams) {
|
| mContext = context;
|
| mServiceNumber = number;
|
| - mInSandbox = inSandbox;
|
| + mSandboxed = sandboxed;
|
| mDeathCallback = deathCallback;
|
| String packageName =
|
| creationParams != null ? creationParams.getPackageName() : context.getPackageName();
|
| mServiceName = new ComponentName(packageName, serviceClassName + mServiceNumber);
|
| mChildProcessCommonParameters = childProcessCommonParameters;
|
| - mAlwaysInForeground = alwaysInForeground;
|
| mCreationParams = creationParams;
|
| - int initialFlags = Context.BIND_AUTO_CREATE;
|
| - if (mAlwaysInForeground) initialFlags |= Context.BIND_IMPORTANT;
|
| - int extraBindFlags = 0;
|
| - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && mCreationParams != null
|
| - && mCreationParams.getIsExternalService()
|
| - && isExportedService(inSandbox, mContext, mServiceName)) {
|
| - extraBindFlags = Context.BIND_EXTERNAL_SERVICE;
|
| - }
|
| - mInitialBinding = new ChildServiceConnection(initialFlags | extraBindFlags);
|
| - mStrongBinding = new ChildServiceConnection(
|
| - Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT | extraBindFlags);
|
| - mWaivedBinding = new ChildServiceConnection(
|
| - Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY | extraBindFlags);
|
| - mModerateBinding = new ChildServiceConnection(Context.BIND_AUTO_CREATE | extraBindFlags);
|
| }
|
|
|
| - private static boolean isExportedService(boolean inSandbox, Context context,
|
| - ComponentName serviceName) {
|
| - // Check for the cached value first. It is assumed that all pooled child services
|
| - // have identical attributes in the manifest.
|
| - final int arrayIndex = inSandbox ? 1 : 0;
|
| - if (sNeedsExtrabindFlags[arrayIndex] != null) {
|
| - return sNeedsExtrabindFlags[arrayIndex].booleanValue();
|
| - }
|
| - boolean result = false;
|
| - try {
|
| - PackageManager packageManager = context.getPackageManager();
|
| - ServiceInfo serviceInfo = packageManager.getServiceInfo(serviceName, 0);
|
| - result = serviceInfo.exported;
|
| - } catch (PackageManager.NameNotFoundException e) {
|
| - Log.e(TAG, "Could not retrieve info about service %s", serviceName, e);
|
| - }
|
| - sNeedsExtrabindFlags[arrayIndex] = Boolean.valueOf(result);
|
| - return result;
|
| + public final Context getContext() {
|
| + return mContext;
|
| }
|
|
|
| - @Override
|
| - public int getServiceNumber() {
|
| + public final int getServiceNumber() {
|
| return mServiceNumber;
|
| }
|
|
|
| - @Override
|
| - public boolean isInSandbox() {
|
| - return mInSandbox;
|
| + public final boolean isSandboxed() {
|
| + return mSandboxed;
|
| }
|
|
|
| - @Override
|
| - public String getPackageName() {
|
| + public final String getPackageName() {
|
| return mCreationParams != null ? mCreationParams.getPackageName()
|
| - : mContext.getPackageName();
|
| + : mContext.getPackageName();
|
| }
|
|
|
| - @Override
|
| - public ChildProcessCreationParams getCreationParams() {
|
| + public final ChildProcessCreationParams getCreationParams() {
|
| return mCreationParams;
|
| }
|
|
|
| - @Override
|
| - public IChildProcessService getService() {
|
| + public final IChildProcessService getService() {
|
| synchronized (mLock) {
|
| return mService;
|
| }
|
| }
|
|
|
| - @Override
|
| + public final ComponentName getServiceName() {
|
| + return mServiceName;
|
| + }
|
| +
|
| + /**
|
| + * @return the connection pid, or 0 if not yet connected
|
| + */
|
| public int getPid() {
|
| synchronized (mLock) {
|
| return mPid;
|
| }
|
| }
|
|
|
| - @Override
|
| - public void start(ChildProcessConnection.StartCallback startCallback) {
|
| + /**
|
| + * Starts a connection to an IChildProcessService. This must be followed by a call to
|
| + * setupConnection() to setup the connection parameters. start() and setupConnection() are
|
| + * separate to allow to pass whatever parameters are available in start(), and complete the
|
| + * remainder later while reducing the connection setup latency.
|
| + * @param startCallback (optional) callback when the child process starts or fails to start.
|
| + */
|
| + public void start(StartCallback startCallback) {
|
| try {
|
| - TraceEvent.begin("ChildProcessConnectionImpl.start");
|
| + TraceEvent.begin("BaseChildProcessConnection.start");
|
| assert LauncherThread.runningOnLauncherThread();
|
| synchronized (mLock) {
|
| - assert mConnectionParams == null :
|
| - "setupConnection() called before start() in ChildProcessConnectionImpl.";
|
| + assert mConnectionParams
|
| + == null
|
| + : "setupConnection() called before start() in BaseChildProcessConnection.";
|
|
|
| mStartCallback = startCallback;
|
|
|
| - if (!mInitialBinding.bind()) {
|
| + if (!bind()) {
|
| Log.e(TAG, "Failed to establish the service connection.");
|
| // We have to notify the caller so that they can free-up associated resources.
|
| // TODO(ppi): Can we hard-fail here?
|
| - mDeathCallback.onChildProcessDied(ChildProcessConnectionImpl.this);
|
| - } else {
|
| - mWaivedBinding.bind();
|
| + mDeathCallback.onChildProcessDied(BaseChildProcessConnection.this);
|
| }
|
| }
|
| } finally {
|
| - TraceEvent.end("ChildProcessConnectionImpl.start");
|
| + TraceEvent.end("BaseChildProcessConnection.start");
|
| }
|
| }
|
|
|
| - @Override
|
| + /**
|
| + * Setups the connection after it was started with start().
|
| + * @param commandLine (optional) will be ignored if the command line was already sent in start()
|
| + * @param filesToBeMapped a list of file descriptors that should be registered
|
| + * @param callback optional client specified callbacks that the child can use to communicate
|
| + * with the parent process
|
| + * @param connectionCallback will be called exactly once after the connection is set up or the
|
| + * setup fails
|
| + */
|
| public void setupConnection(String[] commandLine, FileDescriptorInfo[] filesToBeMapped,
|
| @Nullable IBinder callback, ConnectionCallback connectionCallback) {
|
| assert LauncherThread.runningOnLauncherThread();
|
| @@ -298,11 +331,11 @@ public class ChildProcessConnectionImpl implements ChildProcessConnection {
|
| assert mConnectionParams == null;
|
| if (mServiceDisconnected) {
|
| Log.w(TAG, "Tried to setup a connection that already disconnected.");
|
| - connectionCallback.onConnected(0);
|
| + connectionCallback.onConnected(null);
|
| return;
|
| }
|
| try {
|
| - TraceEvent.begin("ChildProcessConnectionImpl.setupConnection");
|
| + TraceEvent.begin("BaseChildProcessConnection.setupConnection");
|
| mConnectionCallback = connectionCallback;
|
| mConnectionParams = new ConnectionParams(commandLine, filesToBeMapped, callback);
|
| // Run the setup if the service is already connected. If not,
|
| @@ -311,22 +344,19 @@ public class ChildProcessConnectionImpl implements ChildProcessConnection {
|
| doConnectionSetupLocked();
|
| }
|
| } finally {
|
| - TraceEvent.end("ChildProcessConnectionImpl.setupConnection");
|
| + TraceEvent.end("BaseChildProcessConnection.setupConnection");
|
| }
|
| }
|
| }
|
|
|
| - @Override
|
| + /**
|
| + * Terminates the connection to IChildProcessService, closing all bindings. It is safe to call
|
| + * this multiple times.
|
| + */
|
| public void stop() {
|
| synchronized (mLock) {
|
| - mInitialBinding.unbind();
|
| - mStrongBinding.unbind();
|
| - mWaivedBinding.unbind();
|
| - mModerateBinding.unbind();
|
| - mStrongBindingCount = 0;
|
| - if (mService != null) {
|
| - mService = null;
|
| - }
|
| + unbind();
|
| + mService = null;
|
| mConnectionParams = null;
|
| }
|
| }
|
| @@ -341,7 +371,7 @@ public class ChildProcessConnectionImpl implements ChildProcessConnection {
|
| }
|
| try {
|
| TraceEvent.begin(
|
| - "ChildProcessConnectionImpl.ChildServiceConnection.onServiceConnected");
|
| + "BaseChildProcessConnection.ChildServiceConnection.onServiceConnected");
|
| mDidOnServiceConnected = true;
|
| mService = IChildProcessService.Stub.asInterface(service);
|
|
|
| @@ -381,7 +411,7 @@ public class ChildProcessConnectionImpl implements ChildProcessConnection {
|
| }
|
| } finally {
|
| TraceEvent.end(
|
| - "ChildProcessConnectionImpl.ChildServiceConnection.onServiceConnected");
|
| + "BaseChildProcessConnection.ChildServiceConnection.onServiceConnected");
|
| }
|
| }
|
| }
|
| @@ -394,16 +424,14 @@ public class ChildProcessConnectionImpl implements ChildProcessConnection {
|
| if (mServiceDisconnected) {
|
| return;
|
| }
|
| - // Stash the status of the oom bindings, since stop() will release all bindings.
|
| - mWasOomProtected = isCurrentlyOomProtected();
|
| mServiceDisconnected = true;
|
| Log.w(TAG, "onServiceDisconnected (crash or killed by oom): pid=%d", mPid);
|
| stop(); // We don't want to auto-restart on crash. Let the browser do that.
|
| - mDeathCallback.onChildProcessDied(ChildProcessConnectionImpl.this);
|
| + mDeathCallback.onChildProcessDied(BaseChildProcessConnection.this);
|
| // If we have a pending connection callback, we need to communicate the failure to
|
| // the caller.
|
| if (mConnectionCallback != null) {
|
| - mConnectionCallback.onConnected(0);
|
| + mConnectionCallback.onConnected(null);
|
| }
|
| mConnectionCallback = null;
|
| }
|
| @@ -415,7 +443,7 @@ public class ChildProcessConnectionImpl implements ChildProcessConnection {
|
| assert mPid != 0 : "Child service claims to be run by a process of pid=0.";
|
|
|
| if (mConnectionCallback != null) {
|
| - mConnectionCallback.onConnected(mPid);
|
| + mConnectionCallback.onConnected(this);
|
| }
|
| mConnectionCallback = null;
|
| }
|
| @@ -426,9 +454,10 @@ public class ChildProcessConnectionImpl implements ChildProcessConnection {
|
| * connection has been established (as signaled by onServiceConnected()). These two events can
|
| * happen in any order. Has to be called with mLock.
|
| */
|
| + @GuardedBy("mLock")
|
| private void doConnectionSetupLocked() {
|
| try {
|
| - TraceEvent.begin("ChildProcessConnectionImpl.doConnectionSetupLocked");
|
| + TraceEvent.begin("BaseChildProcessConnection.doConnectionSetupLocked");
|
| assert mServiceConnectComplete && mService != null;
|
| assert mConnectionParams != null;
|
|
|
| @@ -460,129 +489,55 @@ public class ChildProcessConnectionImpl implements ChildProcessConnection {
|
| }
|
| mConnectionParams = null;
|
| } finally {
|
| - TraceEvent.end("ChildProcessConnectionImpl.doConnectionSetupLocked");
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public boolean isInitialBindingBound() {
|
| - synchronized (mLock) {
|
| - return mInitialBinding.isBound();
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public boolean isStrongBindingBound() {
|
| - synchronized (mLock) {
|
| - return mStrongBinding.isBound();
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public void removeInitialBinding() {
|
| - synchronized (mLock) {
|
| - assert !mAlwaysInForeground;
|
| - mInitialBinding.unbind();
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public boolean isOomProtectedOrWasWhenDied() {
|
| - synchronized (mLock) {
|
| - if (mServiceDisconnected) {
|
| - return mWasOomProtected;
|
| - } else {
|
| - return isCurrentlyOomProtected();
|
| - }
|
| - }
|
| - }
|
| -
|
| - private boolean isCurrentlyOomProtected() {
|
| - synchronized (mLock) {
|
| - assert !mServiceDisconnected;
|
| - if (mAlwaysInForeground) return ChildProcessLauncher.isApplicationInForeground();
|
| - return mInitialBinding.isBound() || mStrongBinding.isBound();
|
| + TraceEvent.end("BaseChildProcessConnection.doConnectionSetupLocked");
|
| }
|
| }
|
|
|
| - @Override
|
| - public void dropOomBindings() {
|
| - synchronized (mLock) {
|
| - assert !mAlwaysInForeground;
|
| - mInitialBinding.unbind();
|
| -
|
| - mStrongBindingCount = 0;
|
| - mStrongBinding.unbind();
|
| + /** Subclasses should implement this method to bind/unbind to the actual service. */
|
| + protected abstract boolean bind();
|
| + protected abstract void unbind();
|
|
|
| - mModerateBinding.unbind();
|
| - }
|
| + protected ChildServiceConnection createServiceConnection(int bindFlags) {
|
| + return new ChildServiceConnectionImpl(bindFlags);
|
| }
|
|
|
| - @Override
|
| - public void addStrongBinding() {
|
| - synchronized (mLock) {
|
| - if (mService == null) {
|
| - Log.w(TAG, "The connection is not bound for %d", mPid);
|
| - return;
|
| - }
|
| - if (mStrongBindingCount == 0) {
|
| - mStrongBinding.bind();
|
| - }
|
| - mStrongBindingCount++;
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public void removeStrongBinding() {
|
| - synchronized (mLock) {
|
| - if (mService == null) {
|
| - Log.w(TAG, "The connection is not bound for %d", mPid);
|
| - return;
|
| - }
|
| - assert mStrongBindingCount > 0;
|
| - mStrongBindingCount--;
|
| - if (mStrongBindingCount == 0) {
|
| - mStrongBinding.unbind();
|
| - }
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - public boolean isModerateBindingBound() {
|
| - synchronized (mLock) {
|
| - return mModerateBinding.isBound();
|
| - }
|
| + protected boolean shouldBindAsExportedService() {
|
| + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && getCreationParams() != null
|
| + && getCreationParams().getIsExternalService()
|
| + && isExportedService(isSandboxed(), getContext(), getServiceName());
|
| }
|
|
|
| - @Override
|
| - public void addModerateBinding() {
|
| - synchronized (mLock) {
|
| - if (mService == null) {
|
| - Log.w(TAG, "The connection is not bound for %d", mPid);
|
| - return;
|
| - }
|
| - mModerateBinding.bind();
|
| + private static boolean isExportedService(
|
| + boolean inSandbox, Context context, ComponentName serviceName) {
|
| + // Check for the cached value first. It is assumed that all pooled child services
|
| + // have identical attributes in the manifest.
|
| + final int arrayIndex = inSandbox ? 1 : 0;
|
| + if (sNeedsExtrabindFlags[arrayIndex] != null) {
|
| + return sNeedsExtrabindFlags[arrayIndex].booleanValue();
|
| }
|
| - }
|
| -
|
| - @Override
|
| - public void removeModerateBinding() {
|
| - synchronized (mLock) {
|
| - if (mService == null) {
|
| - Log.w(TAG, "The connection is not bound for %d", mPid);
|
| - return;
|
| - }
|
| - mModerateBinding.unbind();
|
| + boolean result = false;
|
| + try {
|
| + PackageManager packageManager = context.getPackageManager();
|
| + ServiceInfo serviceInfo = packageManager.getServiceInfo(serviceName, 0);
|
| + result = serviceInfo.exported;
|
| + } catch (PackageManager.NameNotFoundException e) {
|
| + Log.e(TAG, "Could not retrieve info about service %s", serviceName, e);
|
| }
|
| + sNeedsExtrabindFlags[arrayIndex] = Boolean.valueOf(result);
|
| + return result;
|
| }
|
|
|
| @VisibleForTesting
|
| public void crashServiceForTesting() throws RemoteException {
|
| - mService.crashIntentionallyForTesting();
|
| + synchronized (mLock) {
|
| + mService.crashIntentionallyForTesting();
|
| + }
|
| }
|
|
|
| @VisibleForTesting
|
| public boolean isConnected() {
|
| - return mService != null;
|
| + synchronized (mLock) {
|
| + return mService != null;
|
| + }
|
| }
|
| }
|
|
|