summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt3
-rw-r--r--core/java/android/app/ContextImpl.java49
-rw-r--r--core/java/android/app/LoadedApk.java49
-rw-r--r--core/java/android/content/Context.java23
-rw-r--r--core/java/android/content/ContextWrapper.java12
-rw-r--r--test-mock/src/android/test/mock/MockContext.java11
-rw-r--r--test-runner/src/android/test/IsolatedContext.java19
7 files changed, 129 insertions, 37 deletions
diff --git a/api/current.txt b/api/current.txt
index f170b4a9ee66..ffce57113fbe 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9651,8 +9651,9 @@ package android.content {
public abstract class Context {
ctor public Context();
- method public boolean bindIsolatedService(@RequiresPermission @NonNull android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull String);
+ method public boolean bindIsolatedService(@RequiresPermission @NonNull android.content.Intent, int, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.content.ServiceConnection);
method public abstract boolean bindService(@RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int);
+ method public boolean bindService(@RequiresPermission @NonNull android.content.Intent, int, @NonNull java.util.concurrent.Executor, @NonNull android.content.ServiceConnection);
method @CheckResult(suggest="#enforceCallingOrSelfPermission(String,String)") public abstract int checkCallingOrSelfPermission(@NonNull String);
method @CheckResult(suggest="#enforceCallingOrSelfUriPermission(Uri,int,String)") public abstract int checkCallingOrSelfUriPermission(android.net.Uri, int);
method @CheckResult(suggest="#enforceCallingPermission(String,String)") public abstract int checkCallingPermission(@NonNull String);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index b607f9adebbe..eebac143fd8b 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -144,10 +144,17 @@ class ReceiverRestrictedContext extends ContextWrapper {
}
@Override
- public boolean bindIsolatedService(Intent service, ServiceConnection conn, int flags,
- String instanceName) {
+ public boolean bindService(
+ Intent service, int flags, Executor executor, ServiceConnection conn) {
throw new ReceiverCallNotAllowedException(
- "BroadcastReceiver components are not allowed to bind to services");
+ "BroadcastReceiver components are not allowed to bind to services");
+ }
+
+ @Override
+ public boolean bindIsolatedService(Intent service, int flags, String instanceName,
+ Executor executor, ServiceConnection conn) {
+ throw new ReceiverCallNotAllowedException(
+ "BroadcastReceiver components are not allowed to bind to services");
}
}
@@ -1638,28 +1645,34 @@ class ContextImpl extends Context {
}
@Override
- public boolean bindService(Intent service, ServiceConnection conn,
- int flags) {
+ public boolean bindService(Intent service, ServiceConnection conn, int flags) {
+ warnIfCallingFromSystemProcess();
+ return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null,
+ getUser());
+ }
+
+ @Override
+ public boolean bindService(
+ Intent service, int flags, Executor executor, ServiceConnection conn) {
warnIfCallingFromSystemProcess();
- return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), getUser());
+ return bindServiceCommon(service, conn, flags, null, null, executor, getUser());
}
@Override
- public boolean bindIsolatedService(Intent service, ServiceConnection conn,
- int flags, String instanceName) {
+ public boolean bindIsolatedService(Intent service, int flags, String instanceName,
+ Executor executor, ServiceConnection conn) {
warnIfCallingFromSystemProcess();
if (instanceName == null) {
throw new NullPointerException("null instanceName");
}
- return bindServiceCommon(service, conn, flags, instanceName, mMainThread.getHandler(),
- getUser());
+ return bindServiceCommon(service, conn, flags, instanceName, null, executor, getUser());
}
/** @hide */
@Override
public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
UserHandle user) {
- return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), user);
+ return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null, user);
}
/** @hide */
@@ -1669,7 +1682,7 @@ class ContextImpl extends Context {
if (handler == null) {
throw new IllegalArgumentException("handler must not be null.");
}
- return bindServiceCommon(service, conn, flags, null, handler, user);
+ return bindServiceCommon(service, conn, flags, null, handler, null, user);
}
/** @hide */
@@ -1692,15 +1705,21 @@ class ContextImpl extends Context {
}
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
- String instanceName, Handler
- handler, UserHandle user) {
+ String instanceName, Handler handler, Executor executor, UserHandle user) {
// Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
IServiceConnection sd;
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
+ if (handler != null && executor != null) {
+ throw new IllegalArgumentException("Handler and Executor both supplied");
+ }
if (mPackageInfo != null) {
- sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
+ if (executor != null) {
+ sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);
+ } else {
+ sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
+ }
} else {
throw new RuntimeException("Not supported in system context");
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 5d186a25596f..db8c905eac34 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -76,6 +76,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.Executor;
final class IntentReceiverLeaked extends AndroidRuntimeException {
@UnsupportedAppUsage
@@ -1651,6 +1652,16 @@ public final class LoadedApk {
@UnsupportedAppUsage
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
Context context, Handler handler, int flags) {
+ return getServiceDispatcherCommon(c, context, handler, null, flags);
+ }
+
+ public final IServiceConnection getServiceDispatcher(ServiceConnection c,
+ Context context, Executor executor, int flags) {
+ return getServiceDispatcherCommon(c, context, null, executor, flags);
+ }
+
+ private IServiceConnection getServiceDispatcherCommon(ServiceConnection c,
+ Context context, Handler handler, Executor executor, int flags) {
synchronized (mServices) {
LoadedApk.ServiceDispatcher sd = null;
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
@@ -1659,7 +1670,11 @@ public final class LoadedApk {
sd = map.get(c);
}
if (sd == null) {
- sd = new ServiceDispatcher(c, context, handler, flags);
+ if (executor != null) {
+ sd = new ServiceDispatcher(c, context, executor, flags);
+ } else {
+ sd = new ServiceDispatcher(c, context, handler, flags);
+ }
if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c);
if (map == null) {
map = new ArrayMap<>();
@@ -1667,7 +1682,7 @@ public final class LoadedApk {
}
map.put(c, sd);
} else {
- sd.validate(context, handler);
+ sd.validate(context, handler, executor);
}
return sd.getIServiceConnection();
}
@@ -1744,6 +1759,7 @@ public final class LoadedApk {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final Context mContext;
private final Handler mActivityThread;
+ private final Executor mActivityExecutor;
private final ServiceConnectionLeaked mLocation;
private final int mFlags;
@@ -1783,12 +1799,25 @@ public final class LoadedApk {
mConnection = conn;
mContext = context;
mActivityThread = activityThread;
+ mActivityExecutor = null;
mLocation = new ServiceConnectionLeaked(null);
mLocation.fillInStackTrace();
mFlags = flags;
}
- void validate(Context context, Handler activityThread) {
+ ServiceDispatcher(ServiceConnection conn,
+ Context context, Executor activityExecutor, int flags) {
+ mIServiceConnection = new InnerConnection(this);
+ mConnection = conn;
+ mContext = context;
+ mActivityThread = null;
+ mActivityExecutor = activityExecutor;
+ mLocation = new ServiceConnectionLeaked(null);
+ mLocation.fillInStackTrace();
+ mFlags = flags;
+ }
+
+ void validate(Context context, Handler activityThread, Executor activityExecutor) {
if (mContext != context) {
throw new RuntimeException(
"ServiceConnection " + mConnection +
@@ -1801,6 +1830,12 @@ public final class LoadedApk {
" registered with differing handler (was " +
mActivityThread + " now " + activityThread + ")");
}
+ if (mActivityExecutor != activityExecutor) {
+ throw new RuntimeException(
+ "ServiceConnection " + mConnection +
+ " registered with differing executor (was " +
+ mActivityExecutor + " now " + activityExecutor + ")");
+ }
}
void doForget() {
@@ -1840,7 +1875,9 @@ public final class LoadedApk {
}
public void connected(ComponentName name, IBinder service, boolean dead) {
- if (mActivityThread != null) {
+ if (mActivityExecutor != null) {
+ mActivityExecutor.execute(new RunConnection(name, service, 0, dead));
+ } else if (mActivityThread != null) {
mActivityThread.post(new RunConnection(name, service, 0, dead));
} else {
doConnected(name, service, dead);
@@ -1848,7 +1885,9 @@ public final class LoadedApk {
}
public void death(ComponentName name, IBinder service) {
- if (mActivityThread != null) {
+ if (mActivityExecutor != null) {
+ mActivityExecutor.execute(new RunConnection(name, service, 1, false));
+ } else if (mActivityThread != null) {
mActivityThread.post(new RunConnection(name, service, 1, false));
} else {
doDeath(name, service);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index beb1fb68d218..a5a8cd704718 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -17,6 +17,7 @@
package android.content;
import android.annotation.AttrRes;
+import android.annotation.CallbackExecutor;
import android.annotation.CheckResult;
import android.annotation.ColorInt;
import android.annotation.ColorRes;
@@ -2966,26 +2967,40 @@ public abstract class Context {
@NonNull ServiceConnection conn, @BindServiceFlags int flags);
/**
+ * Same as {@link #bindService(Intent, ServiceConnection, int)} with executor to control
+ * ServiceConnection callbacks.
+ * @param executor Callbacks on ServiceConnection will be called on executor. Must use same
+ * instance for the same instance of ServiceConnection.
+ */
+ public boolean bindService(@RequiresPermission @NonNull Intent service,
+ @BindServiceFlags int flags, @NonNull @CallbackExecutor Executor executor,
+ @NonNull ServiceConnection conn) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* Variation of {@link #bindService} that, in the specific case of isolated
* services, allows the caller to generate multiple instances of a service
* from a single component declaration.
*
* @param service Identifies the service to connect to. The Intent must
* specify an explicit component name.
- * @param conn Receives information as the service is started and stopped.
- * This must be a valid ServiceConnection object; it must not be null.
* @param flags Operation options for the binding as per {@link #bindService}.
* @param instanceName Unique identifier for the service instance. Each unique
* name here will result in a different service instance being created.
* @return Returns success of binding as per {@link #bindService}.
+ * @param executor Callbacks on ServiceConnection will be called on executor.
+ * Must use same instance for the same instance of ServiceConnection.
+ * @param conn Receives information as the service is started and stopped.
+ * This must be a valid ServiceConnection object; it must not be null.
*
* @throws SecurityException If the caller does not have permission to access the service
*
* @see #bindService
*/
public boolean bindIsolatedService(@RequiresPermission @NonNull Intent service,
- @NonNull ServiceConnection conn, @BindServiceFlags int flags,
- @NonNull String instanceName) {
+ @BindServiceFlags int flags, @NonNull String instanceName,
+ @NonNull @CallbackExecutor Executor executor, @NonNull ServiceConnection conn) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 40559d31d631..0859f97e81a1 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -706,9 +706,15 @@ public class ContextWrapper extends Context {
}
@Override
- public boolean bindIsolatedService(Intent service, ServiceConnection conn,
- int flags, String instanceName) {
- return mBase.bindIsolatedService(service, conn, flags, instanceName);
+ public boolean bindService(Intent service, int flags, Executor executor,
+ ServiceConnection conn) {
+ return mBase.bindService(service, flags, executor, conn);
+ }
+
+ @Override
+ public boolean bindIsolatedService(Intent service, int flags, String instanceName,
+ Executor executor, ServiceConnection conn) {
+ return mBase.bindIsolatedService(service, flags, instanceName, executor, conn);
}
/** @hide */
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index ae6cd29fb2de..a95b6f11e98a 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -577,9 +577,14 @@ public class MockContext extends Context {
}
@Override
- public boolean bindIsolatedService(Intent service,
- ServiceConnection conn, int flags,
- String instanceName) {
+ public boolean bindService(Intent service, int flags, Executor executor,
+ ServiceConnection conn) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean bindIsolatedService(Intent service, int flags, String instanceName,
+ Executor executor, ServiceConnection conn) {
throw new UnsupportedOperationException();
}
diff --git a/test-runner/src/android/test/IsolatedContext.java b/test-runner/src/android/test/IsolatedContext.java
index 73db4517e130..dd4a9a3a4d69 100644
--- a/test-runner/src/android/test/IsolatedContext.java
+++ b/test-runner/src/android/test/IsolatedContext.java
@@ -17,13 +17,13 @@
package android.test;
import android.accounts.AccountManager;
-import android.content.ContextWrapper;
+import android.content.BroadcastReceiver;
import android.content.ContentResolver;
-import android.content.Intent;
import android.content.Context;
-import android.content.ServiceConnection;
-import android.content.BroadcastReceiver;
+import android.content.ContextWrapper;
+import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.test.mock.MockAccountManager;
@@ -31,6 +31,7 @@ import android.test.mock.MockAccountManager;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
/**
@@ -75,8 +76,14 @@ public class IsolatedContext extends ContextWrapper {
}
@Override
- public boolean bindIsolatedService(Intent service, ServiceConnection conn, int flags,
- String instanceName) {
+ public boolean bindService(Intent service, int flags, Executor executor,
+ ServiceConnection conn) {
+ return false;
+ }
+
+ @Override
+ public boolean bindIsolatedService(Intent service, int flags, String instanceName,
+ Executor executor, ServiceConnection conn) {
return false;
}