Chromium Code Reviews| 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 62% |
| 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..1155e17d7cc2c6848d8b1b6ba88155fcc0053b16 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 |
| @@ -10,7 +10,6 @@ import android.content.Intent; |
| import android.content.ServiceConnection; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ServiceInfo; |
| -import android.os.Build; |
| import android.os.Bundle; |
| import android.os.IBinder; |
| import android.os.RemoteException; |
| @@ -26,97 +25,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; |
| - |
| - // 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; |
| +public abstract class BaseChildProcessConnection { |
| + private static final String TAG = "BaseChildProcessConn"; |
| - 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 +101,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 +140,7 @@ public class ChildProcessConnectionImpl implements ChildProcessConnection { |
| LauncherThread.post(new Runnable() { |
| @Override |
| public void run() { |
| - ChildProcessConnectionImpl.this.onServiceConnectedOnLauncherThread(service); |
| + BaseChildProcessConnection.this.onServiceConnectedOnLauncherThread(service); |
| } |
| }); |
| } |
| @@ -174,123 +151,182 @@ 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; |
|
boliu
2017/04/20 22:33:34
style nit: no blank lines in between
Jay Civelli
2017/04/25 06:02:46
I removed some of them. I am not sure I understand
|
| + |
| + 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. |
| + private final Object mLock = new Object(); |
|
boliu
2017/04/20 22:33:34
I think it only became clear after this refactor,
Jay Civelli
2017/04/25 06:02:46
Will do in a separate CL. Filed bug and added TODO
|
| + |
| + // 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 Context getContext() { |
|
boliu
2017/04/20 22:33:34
mark methods like this that are not meant to be ov
Jay Civelli
2017/04/25 06:02:46
Done.
|
| + return mContext; |
| } |
| - @Override |
| public int getServiceNumber() { |
| return mServiceNumber; |
| } |
| - @Override |
| - public boolean isInSandbox() { |
| - return mInSandbox; |
| + public boolean isSandboxed() { |
| + return mSandboxed; |
| } |
| - @Override |
| public String getPackageName() { |
| return mCreationParams != null ? mCreationParams.getPackageName() |
| - : mContext.getPackageName(); |
| + : mContext.getPackageName(); |
| } |
| - @Override |
| public ChildProcessCreationParams getCreationParams() { |
| return mCreationParams; |
| } |
| - @Override |
| public IChildProcessService getService() { |
| synchronized (mLock) { |
| return mService; |
| } |
| } |
| - @Override |
| + public 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 +334,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,19 +347,18 @@ 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; |
| + unbind(); |
| if (mService != null) { |
| mService = null; |
| } |
| @@ -341,7 +376,7 @@ public class ChildProcessConnectionImpl implements ChildProcessConnection { |
| } |
| try { |
| TraceEvent.begin( |
| - "ChildProcessConnectionImpl.ChildServiceConnection.onServiceConnected"); |
| + "BaseChildProcessConnection.ChildServiceConnection.onServiceConnected"); |
| mDidOnServiceConnected = true; |
| mService = IChildProcessService.Stub.asInterface(service); |
| @@ -381,7 +416,7 @@ public class ChildProcessConnectionImpl implements ChildProcessConnection { |
| } |
| } finally { |
| TraceEvent.end( |
| - "ChildProcessConnectionImpl.ChildServiceConnection.onServiceConnected"); |
| + "BaseChildProcessConnection.ChildServiceConnection.onServiceConnected"); |
| } |
| } |
| } |
| @@ -394,16 +429,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 +448,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 +459,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 +494,49 @@ public class ChildProcessConnectionImpl implements ChildProcessConnection { |
| } |
| mConnectionParams = null; |
| } finally { |
| - TraceEvent.end("ChildProcessConnectionImpl.doConnectionSetupLocked"); |
| + TraceEvent.end("BaseChildProcessConnection.doConnectionSetupLocked"); |
| } |
| } |
| - @Override |
| - public boolean isInitialBindingBound() { |
| - synchronized (mLock) { |
| - return mInitialBinding.isBound(); |
| - } |
| - } |
| - |
| - @Override |
| - public boolean isStrongBindingBound() { |
| - synchronized (mLock) { |
| - return mStrongBinding.isBound(); |
| - } |
| - } |
| + /** Subclasses should implement this method to bind/unbind to the actual service. */ |
| + protected abstract boolean bind(); |
| + protected abstract void unbind(); |
| - @Override |
| - public void removeInitialBinding() { |
| - synchronized (mLock) { |
| - assert !mAlwaysInForeground; |
| - mInitialBinding.unbind(); |
| - } |
| + protected ChildServiceConnection createServiceConnection(int bindFlags) { |
| + return new ChildServiceConnectionImpl(bindFlags); |
| } |
| - @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(); |
| - } |
| - } |
| - |
| - @Override |
| - public void dropOomBindings() { |
| - synchronized (mLock) { |
| - assert !mAlwaysInForeground; |
| - mInitialBinding.unbind(); |
| - |
| - mStrongBindingCount = 0; |
| - mStrongBinding.unbind(); |
| - |
| - mModerateBinding.unbind(); |
| - } |
| - } |
| - |
| - @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(); |
| - } |
| - } |
| - |
| - @Override |
| - public void addModerateBinding() { |
| - synchronized (mLock) { |
| - if (mService == null) { |
| - Log.w(TAG, "The connection is not bound for %d", mPid); |
| - return; |
| - } |
| - mModerateBinding.bind(); |
| + protected 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; |
| + } |
| } |
| } |