From f824a962c4a603e3de5859af8a8ade499f60f7ce Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Thu, 11 May 2023 14:41:49 +0800 Subject: [PATCH 01/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server. --- binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java index 214eb6dc4c5..a1b15b5f210 100644 --- a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java +++ b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java @@ -20,6 +20,7 @@ import static com.google.common.base.Preconditions.checkState; import android.content.Context; +import android.os.UserHandle; import androidx.core.content.ContextCompat; import com.google.errorprone.annotations.DoNotCall; import io.grpc.ChannelCredentials; @@ -121,6 +122,7 @@ public static BinderChannelBuilder forTarget(String target) { private SecurityPolicy securityPolicy; private InboundParcelablePolicy inboundParcelablePolicy; private BindServiceFlags bindServiceFlags; + private UserHandle userHandle; private boolean strictLifecycleManagement; private BinderChannelBuilder( From 334e998dcdace3cd5afbc809f6d98e0a5add63f3 Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Thu, 11 May 2023 14:41:49 +0800 Subject: [PATCH 02/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server. --- .../grpc/binder/BinderChannelCredentials.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java b/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java new file mode 100644 index 00000000000..a3b396a28d1 --- /dev/null +++ b/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java @@ -0,0 +1,55 @@ + +package io.grpc.binder; + +import android.content.ComponentName; +import android.content.Context; +import io.grpc.ChannelCredentials; +import io.grpc.ExperimentalApi; +import javax.annotation.Nullable; + +/** Additional arbitrary arguments to establish a Android binder connection channel. */ +@ExperimentalApi("https://github.com/grpc/grpc-java/issues/10173") +public final class BinderChannelCredentials extends ChannelCredentials { + + /** + * Creates the default BinderChannelCredentials. + * + * @param sourceContext the context to bind from (e.g. The current Activity or Application). + * @return a BinderChannelCredentials + */ + public static BinderChannelCredentials forDefault(Context sourceContext) { + return new BinderChannelCredentials(sourceContext, null); + } + + /** + * Creates a BinderChannelCredentials to be used with DevicePolicyManager API. + * + * @param sourceContext the context to bind from (e.g. The current Activity or Application). + * @param devicePolicyAdminComponentName the admin component to be specified with + * DevicePolicyManager.bindDeviceAdminServiceAsUser API. + * @return a BinderChannelCredentials + */ + public static BinderChannelCredentials forDevicePolicyAdmin( + Context sourceContext, ComponentName devicePolicyAdminComponentName) { + return new BinderChannelCredentials(sourceContext, devicePolicyAdminComponentName); + } + + private final Context sourceContext; + @Nullable private final ComponentName devicePolicyAdminComponentName; + + private BinderChannelCredentials( + Context sourceContext, @Nullable ComponentName devicePolicyAdminComponentName) { + this.sourceContext = sourceContext; + this.devicePolicyAdminComponentName = devicePolicyAdminComponentName; + } + + @Override + public ChannelCredentials withoutBearerTokens() { + return this; + } + + @Nullable + public ComponentName getDevicePolicyAdminComponentName() { + return devicePolicyAdminComponentName; + } +} From 19de143accbd340125eea7bba3fb736d37e23f52 Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Thu, 11 May 2023 14:41:49 +0800 Subject: [PATCH 03/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server. --- .../io/grpc/binder/BinderChannelBuilder.java | 99 +++++++++++++++++-- .../grpc/binder/internal/BinderTransport.java | 30 ++++++ .../grpc/binder/internal/ServiceBinding.java | 66 +++++++++++-- 3 files changed, 179 insertions(+), 16 deletions(-) diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java index a1b15b5f210..b956792d1f1 100644 --- a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java +++ b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java @@ -72,7 +72,36 @@ public final class BinderChannelBuilder public static BinderChannelBuilder forAddress( AndroidComponentAddress directAddress, Context sourceContext) { return new BinderChannelBuilder( - checkNotNull(directAddress, "directAddress"), null, sourceContext); + checkNotNull(directAddress, "directAddress"), + null, + sourceContext, + BinderChannelCredentials.forDefault(sourceContext)); + } + + /** + * Creates a channel builder that will bind to a remote Android service with provided + * BinderChannelCredentials. + * + *

The underlying Android binding will be torn down when the channel becomes idle. This happens + * after 30 minutes without use by default but can be configured via {@link + * ManagedChannelBuilder#idleTimeout(long, TimeUnit)} or triggered manually with {@link + * ManagedChannel#enterIdle()}. + * + *

You the caller are responsible for managing the lifecycle of any channels built by the + * resulting builder. They will not be shut down automatically. + * + * @param directAddress the {@link AndroidComponentAddress} referencing the service to bind to. + * @param sourceContext the context to bind from (e.g. The current Activity or Application). + * @param channelCredentials the arbitrary binder specific channel credentials to be used to + * establish a binder connection. + * @return a new builder + */ + public static BinderChannelBuilder forAddress( + AndroidComponentAddress directAddress, + Context sourceContext, + BinderChannelCredentials channelCredentials) { + return new BinderChannelBuilder( + checkNotNull(directAddress, "directAddress"), null, sourceContext, channelCredentials); } /** @@ -93,7 +122,36 @@ public static BinderChannelBuilder forAddress( * @return a new builder */ public static BinderChannelBuilder forTarget(String target, Context sourceContext) { - return new BinderChannelBuilder(null, checkNotNull(target, "target"), sourceContext); + return new BinderChannelBuilder( + null, + checkNotNull(target, "target"), + sourceContext, + BinderChannelCredentials.forDefault(sourceContext)); + } + + /** + * Creates a channel builder that will bind to a remote Android service, via a string target name + * which will be resolved. + * + *

The underlying Android binding will be torn down when the channel becomes idle. This happens + * after 30 minutes without use by default but can be configured via {@link + * ManagedChannelBuilder#idleTimeout(long, TimeUnit)} or triggered manually with {@link + * ManagedChannel#enterIdle()}. + * + *

You the caller are responsible for managing the lifecycle of any channels built by the + * resulting builder. They will not be shut down automatically. + * + * @param target A target uri which should resolve into an {@link AndroidComponentAddress} + * referencing the service to bind to. + * @param channelCredentials the arbitrary binder specific channel credentials to be used to + * establish a binder connection. + * @param sourceContext the context to bind from (e.g. The current Activity or Application). + * @return a new builder + */ + public static BinderChannelBuilder forTarget( + String target, Context sourceContext, BinderChannelCredentials channelCredentials) { + return new BinderChannelBuilder( + null, checkNotNull(target, "target"), sourceContext, channelCredentials); } /** @@ -115,6 +173,7 @@ public static BinderChannelBuilder forTarget(String target) { } private final ManagedChannelImplBuilder managedChannelImplBuilder; + private final BinderChannelCredentials channelCredentials; private Executor mainThreadExecutor; private ObjectPool schedulerPool = @@ -122,18 +181,20 @@ public static BinderChannelBuilder forTarget(String target) { private SecurityPolicy securityPolicy; private InboundParcelablePolicy inboundParcelablePolicy; private BindServiceFlags bindServiceFlags; - private UserHandle userHandle; + @Nullable private UserHandle userHandle; private boolean strictLifecycleManagement; private BinderChannelBuilder( @Nullable AndroidComponentAddress directAddress, @Nullable String target, - Context sourceContext) { + Context sourceContext, + BinderChannelCredentials channelCredentials) { mainThreadExecutor = ContextCompat.getMainExecutor(checkNotNull(sourceContext, "sourceContext")); securityPolicy = SecurityPolicies.internalOnly(); inboundParcelablePolicy = InboundParcelablePolicy.DEFAULT; bindServiceFlags = BindServiceFlags.DEFAULTS; + this.channelCredentials = channelCredentials; final class BinderChannelTransportFactoryBuilder implements ClientTransportFactoryBuilder { @@ -146,7 +207,9 @@ public ClientTransportFactory buildClientTransportFactory() { managedChannelImplBuilder.getOffloadExecutorPool(), securityPolicy, bindServiceFlags, - inboundParcelablePolicy); + inboundParcelablePolicy, + channelCredentials, + targetUserHandle); } } @@ -218,6 +281,22 @@ public BinderChannelBuilder securityPolicy(SecurityPolicy securityPolicy) { return this; } +/** + * Provides the target {@UserHandle} of the remote Android service. + * + *

When targetUserHandle is set, Context.bindServiceAsUser will used and additional Android + * permissions will be required. If your usage does not require cross-user communications, please + * do not set this field. It is the caller's responsibility to make sure that it holds the + * corresponding permissions. + * + * @param targetUserHandle the target user to bind into. + * @return this + */ + public BinderChannelBuilder bindForUser(UserHandle targetUserHandle) { + this.targetUserHandle = targetUserHandle; + return this; + } + /** Sets the policy for inbound parcelable objects. */ public BinderChannelBuilder inboundParcelablePolicy( InboundParcelablePolicy inboundParcelablePolicy) { @@ -265,7 +344,9 @@ private static final class TransportFactory implements ClientTransportFactory { ObjectPool offloadExecutorPool, SecurityPolicy securityPolicy, BindServiceFlags bindServiceFlags, - InboundParcelablePolicy inboundParcelablePolicy) { + InboundParcelablePolicy inboundParcelablePolicy, + BinderChannelCredentials channelCredentials, + @Nullable UserHandle targetUserHandle) { this.sourceContext = sourceContext; this.mainThreadExecutor = mainThreadExecutor; this.scheduledExecutorPool = scheduledExecutorPool; @@ -273,6 +354,8 @@ private static final class TransportFactory implements ClientTransportFactory { this.securityPolicy = securityPolicy; this.bindServiceFlags = bindServiceFlags; this.inboundParcelablePolicy = inboundParcelablePolicy; + this.channelCredentials = channelCredentials; + this.targetUserHandle = targetUserHandle; executorService = scheduledExecutorPool.getObject(); offloadExecutor = offloadExecutorPool.getObject(); @@ -293,7 +376,9 @@ public ConnectionClientTransport newClientTransport( offloadExecutorPool, securityPolicy, inboundParcelablePolicy, - options.getEagAttributes()); + options.getEagAttributes(), + channelCredentials, + targetUserHandle); } @Override diff --git a/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java b/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java index 70b89165174..558cddec4c2 100644 --- a/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java +++ b/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java @@ -28,6 +28,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.TransactionTooLargeException; +import android.os.UserHandle; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Ticker; import com.google.common.util.concurrent.ListenableFuture; @@ -46,6 +47,7 @@ import io.grpc.StatusException; import io.grpc.binder.AndroidComponentAddress; import io.grpc.binder.BindServiceFlags; +import io.grpc.binder.BinderChannelCredentials; import io.grpc.binder.InboundParcelablePolicy; import io.grpc.binder.SecurityPolicy; import io.grpc.internal.ClientStream; @@ -576,6 +578,32 @@ public BinderClientTransport( SecurityPolicy securityPolicy, InboundParcelablePolicy inboundParcelablePolicy, Attributes eagAttrs) { + this( + sourceContext, + targetAddress, + bindServiceFlags, + mainThreadExecutor, + executorServicePool, + offloadExecutorPool, + securityPolicy, + inboundParcelablePolicy, + eagAttrs, + BinderChannelCredentials.forDefault(sourceContext), + null); + } + + public BinderClientTransport( + Context sourceContext, + AndroidComponentAddress targetAddress, + BindServiceFlags bindServiceFlags, + Executor mainThreadExecutor, + ObjectPool executorServicePool, + ObjectPool offloadExecutorPool, + SecurityPolicy securityPolicy, + InboundParcelablePolicy inboundParcelablePolicy, + Attributes eagAttrs, + BinderChannelCredentials channelCredentials, + @Nullable UserHandle targetUserHandle) { super( executorServicePool, buildClientAttributes(eagAttrs, sourceContext, targetAddress, inboundParcelablePolicy), @@ -592,6 +620,8 @@ public BinderClientTransport( sourceContext, targetAddress.asBindIntent(), bindServiceFlags.toInteger(), + channelCredentials, + targetUserHandle, this); } diff --git a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java index 650ead9bcdb..4ceab44d391 100644 --- a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java +++ b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java @@ -16,15 +16,20 @@ package io.grpc.binder.internal; +import static com.google.common.base.Preconditions.checkState; + +import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; +import android.os.UserHandle; import androidx.annotation.AnyThread; import androidx.annotation.MainThread; import com.google.common.annotations.VisibleForTesting; import io.grpc.Status; +import io.grpc.binder.BinderChannelCredentials; import java.util.concurrent.Executor; import java.util.logging.Level; import java.util.logging.Logger; @@ -60,6 +65,8 @@ private enum State { private final int bindFlags; private final Observer observer; private final Executor mainThreadExecutor; + private final BinderChannelCredentials channelCredentials; + @Nullable private final UserHandle targetUserHandle; @GuardedBy("this") private State state; @@ -78,6 +85,8 @@ private enum State { Context sourceContext, Intent bindIntent, int bindFlags, + BinderChannelCredentials channelCredentials, + @Nullable UserHandle targetUserHandle, Observer observer) { // We need to synchronize here ensure other threads see all // non-final fields initialized after the constructor. @@ -87,6 +96,8 @@ private enum State { this.observer = observer; this.sourceContext = sourceContext; this.mainThreadExecutor = mainThreadExecutor; + this.channelCredentials = channelCredentials; + this.targetUserHandle = targetUserHandle; state = State.NOT_BINDING; reportedState = State.NOT_BINDING; } @@ -117,7 +128,9 @@ private void notifyUnbound(Status reason) { public synchronized void bind() { if (state == State.NOT_BINDING) { state = State.BINDING; - Status bindResult = bindInternal(sourceContext, bindIntent, this, bindFlags); + Status bindResult = + bindInternal( + sourceContext, bindIntent, this, bindFlags, channelCredentials, targetUserHandle); if (!bindResult.isOk()) { handleBindServiceFailure(sourceContext, this); state = State.UNBOUND; @@ -127,19 +140,54 @@ public synchronized void bind() { } private static Status bindInternal( - Context context, Intent bindIntent, ServiceConnection conn, int flags) { + Context context, + Intent bindIntent, + ServiceConnection conn, + int flags, + BinderChannelCredentials channelCredentials, + @Nullable UserHandle targetUserHandle) { + String methodName = "bindService"; try { - if (!context.bindService(bindIntent, conn, flags)) { - return Status.UNIMPLEMENTED.withDescription( - "bindService(" + bindIntent + ") returned false"); + if (targetUserHandle == null) { + checkState( + channelCredentials.getDevicePolicyAdminComponentName() == null, + "BindingChannelCredentials is expected to have null devicePolicyAdmin when" + + " targetUserHandle is not set"); + if (!context.bindService(bindIntent, conn, flags)) { + return Status.UNIMPLEMENTED.withDescription( + "bindService(" + bindIntent + ") returned false"); + } + } else { + if (channelCredentials.getDevicePolicyAdminComponentName() != null) { + methodName = "DevicePolicyManager.bindDeviceAdminServiceAsUser"; + DevicePolicyManager devicePolicyManager = + context.getSystemService(DevicePolicyManager.class); + if (!devicePolicyManager.bindDeviceAdminServiceAsUser( + channelCredentials.getDevicePolicyAdminComponentName(), + bindIntent, + conn, + flags, + targetUserHandle)) { + return Status.UNIMPLEMENTED.withDescription( + "DevicePolicyManager.bindDeviceAdminServiceAsUser(" + + bindIntent + + ") returned false"); + } + } else { + methodName = "bindServiceAsUser"; + if (!context.bindServiceAsUser(bindIntent, conn, flags, targetUserHandle)) { + return Status.UNIMPLEMENTED.withDescription( + "bindServiceAsUser(" + bindIntent + ") returned false"); + } + } } return Status.OK; } catch (SecurityException e) { - return Status.PERMISSION_DENIED.withCause(e).withDescription( - "SecurityException from bindService"); + return Status.PERMISSION_DENIED + .withCause(e) + .withDescription("SecurityException from " + methodName); } catch (RuntimeException e) { - return Status.INTERNAL.withCause(e).withDescription( - "RuntimeException from bindService"); + return Status.INTERNAL.withCause(e).withDescription("RuntimeException from " + methodName); } } From a685c7afe97854020ab04e395a514410ba8c91ec Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Thu, 11 May 2023 14:41:49 +0800 Subject: [PATCH 04/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server. --- .../binder/internal/ServiceBindingTest.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java b/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java index 967e91b820d..9e2496087e2 100644 --- a/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java +++ b/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java @@ -24,6 +24,7 @@ import static org.robolectric.annotation.LooperMode.Mode.PAUSED; import android.app.Application; +import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -32,6 +33,7 @@ import androidx.test.core.app.ApplicationProvider; import io.grpc.Status; import io.grpc.Status.Code; +import io.grpc.binder.BinderChannelCredentials; import io.grpc.binder.internal.Bindable.Observer; import org.junit.Before; import org.junit.Rule; @@ -44,6 +46,7 @@ import org.robolectric.annotation.Config; import org.robolectric.annotation.LooperMode; import org.robolectric.shadows.ShadowApplication; +import org.robolectric.shadows.ShadowDevicePolicyManager; @LooperMode(PAUSED) @RunWith(RobolectricTestRunner.class) @@ -256,6 +259,45 @@ public void testCallsAfterUnbindDontCrash() throws Exception { shadowOf(getMainLooper()).idle(); } + @Test + public void testBindWithTargetUserHandle() throws Exception { + binding = + newBuilder().setTargetUserHandle(UserHandle.getUserHandleForUid(/* userId= */ 0)).build(); + shadowOf(getMainLooper()).idle(); + + binding.bind(); + shadowOf(getMainLooper()).idle(); + + assertThat(shadowApplication.getBoundServiceConnections()).isNotEmpty(); + assertThat(observer.gotBoundEvent).isTrue(); + assertThat(observer.binder).isSameInstanceAs(mockBinder); + assertThat(observer.gotUnboundEvent).isFalse(); + assertThat(binding.isSourceContextCleared()).isFalse(); + } + + @Test + public void testBindWithDeviceAdmin() throws Exception { + String deviceAdminClassName = "DevicePolicyAdmin"; + ComponentName adminComponent = new ComponentName(appContext, deviceAdminClassName); + allowBindDeviceAdminForUser(appContext, adminComponent, /* userId= */ 0); + binding = + newBuilder() + .setTargetUserHandle(UserHandle.getUserHandleForUid(/* userId= */ 0)) + .setChannelCredentials( + BinderChannelCredentials.forDevicePolicyAdmin(appContext, adminComponent)) + .build(); + shadowOf(getMainLooper()).idle(); + + binding.bind(); + shadowOf(getMainLooper()).idle(); + + assertThat(shadowApplication.getBoundServiceConnections()).isNotEmpty(); + assertThat(observer.gotBoundEvent).isTrue(); + assertThat(observer.binder).isSameInstanceAs(mockBinder); + assertThat(observer.gotUnboundEvent).isFalse(); + assertThat(binding.isSourceContextCleared()).isFalse(); + } + private void assertNoLockHeld() { try { binding.wait(1); @@ -268,6 +310,14 @@ private void assertNoLockHeld() { } } + private static void allowBindDeviceAdminForUser(Context context, ComponentName admin, int userId) { + ShadowDevicePolicyManager devicePolicyManager = + shadowOf(context.getSystemService(DevicePolicyManager.class)); + devicePolicyManager.setDeviceOwner(admin); + devicePolicyManager.setBindDeviceAdminTargetUsers( + Arrays.asList(UserHandle.getUserHandleForUid(userId))); + } + private class TestObserver implements Bindable.Observer { public boolean gotBoundEvent; @@ -298,9 +348,12 @@ private static class ServiceBindingBuilder { private Observer observer; private Intent bindIntent = new Intent(); private int bindServiceFlags; + @Nullable private UserHandle targetUserHandle = null; + private BinderChannelCredentials channelCredentials; public ServiceBindingBuilder setSourceContext(Context sourceContext) { this.sourceContext = sourceContext; + this.channelCredentials = BinderChannelCredentials.forDefault(sourceContext); return this; } @@ -324,12 +377,25 @@ public ServiceBindingBuilder setObserver(Observer observer) { return this; } + public ServiceBindingBuilder setTargetUserHandle(UserHandle targetUserHandle) { + this.targetUserHandle = targetUserHandle; + return this; + } + + public ServiceBindingBuilder setChannelCredentials( + BinderChannelCredentials channelCredentials) { + this.channelCredentials = channelCredentials; + return this; + } + public ServiceBinding build() { return new ServiceBinding( ContextCompat.getMainExecutor(sourceContext), sourceContext, bindIntent, bindServiceFlags, + channelCredentials, + targetUserHandle, observer); } } From 039f62746aee4346fe9d427853208e789e160fce Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Thu, 11 May 2023 14:41:49 +0800 Subject: [PATCH 05/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server. --- .../io/grpc/binder/BinderChannelBuilder.java | 40 ++++++++----------- .../grpc/binder/BinderChannelCredentials.java | 4 ++ .../grpc/binder/internal/BinderTransport.java | 35 +++------------- .../grpc/binder/internal/ServiceBinding.java | 15 ++++--- .../binder/internal/ServiceBindingTest.java | 28 +++++++++++-- 5 files changed, 56 insertions(+), 66 deletions(-) diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java index b956792d1f1..67e9080864f 100644 --- a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java +++ b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java @@ -74,7 +74,6 @@ public static BinderChannelBuilder forAddress( return new BinderChannelBuilder( checkNotNull(directAddress, "directAddress"), null, - sourceContext, BinderChannelCredentials.forDefault(sourceContext)); } @@ -91,17 +90,16 @@ public static BinderChannelBuilder forAddress( * resulting builder. They will not be shut down automatically. * * @param directAddress the {@link AndroidComponentAddress} referencing the service to bind to. - * @param sourceContext the context to bind from (e.g. The current Activity or Application). * @param channelCredentials the arbitrary binder specific channel credentials to be used to - * establish a binder connection. + * establish a binder connection including the context to bind from (e.g. The current + * Activity or Application). * @return a new builder */ public static BinderChannelBuilder forAddress( AndroidComponentAddress directAddress, - Context sourceContext, BinderChannelCredentials channelCredentials) { return new BinderChannelBuilder( - checkNotNull(directAddress, "directAddress"), null, sourceContext, channelCredentials); + checkNotNull(directAddress, "directAddress"), null, channelCredentials); } /** @@ -125,7 +123,6 @@ public static BinderChannelBuilder forTarget(String target, Context sourceContex return new BinderChannelBuilder( null, checkNotNull(target, "target"), - sourceContext, BinderChannelCredentials.forDefault(sourceContext)); } @@ -144,14 +141,14 @@ public static BinderChannelBuilder forTarget(String target, Context sourceContex * @param target A target uri which should resolve into an {@link AndroidComponentAddress} * referencing the service to bind to. * @param channelCredentials the arbitrary binder specific channel credentials to be used to - * establish a binder connection. - * @param sourceContext the context to bind from (e.g. The current Activity or Application). + * establish a binder connection including the context to bind from (e.g. The current + * Activity or Application). * @return a new builder */ public static BinderChannelBuilder forTarget( - String target, Context sourceContext, BinderChannelCredentials channelCredentials) { + String target, BinderChannelCredentials channelCredentials) { return new BinderChannelBuilder( - null, checkNotNull(target, "target"), sourceContext, channelCredentials); + null, checkNotNull(target, "target"), channelCredentials); } /** @@ -173,7 +170,6 @@ public static BinderChannelBuilder forTarget(String target) { } private final ManagedChannelImplBuilder managedChannelImplBuilder; - private final BinderChannelCredentials channelCredentials; private Executor mainThreadExecutor; private ObjectPool schedulerPool = @@ -181,34 +177,32 @@ public static BinderChannelBuilder forTarget(String target) { private SecurityPolicy securityPolicy; private InboundParcelablePolicy inboundParcelablePolicy; private BindServiceFlags bindServiceFlags; - @Nullable private UserHandle userHandle; + @Nullable private UserHandle targetUserHandle; private boolean strictLifecycleManagement; private BinderChannelBuilder( @Nullable AndroidComponentAddress directAddress, @Nullable String target, - Context sourceContext, BinderChannelCredentials channelCredentials) { mainThreadExecutor = - ContextCompat.getMainExecutor(checkNotNull(sourceContext, "sourceContext")); + ContextCompat.getMainExecutor( + checkNotNull(channelCredentials.getSourceContext(), "sourceContext")); securityPolicy = SecurityPolicies.internalOnly(); inboundParcelablePolicy = InboundParcelablePolicy.DEFAULT; bindServiceFlags = BindServiceFlags.DEFAULTS; - this.channelCredentials = channelCredentials; final class BinderChannelTransportFactoryBuilder implements ClientTransportFactoryBuilder { @Override public ClientTransportFactory buildClientTransportFactory() { return new TransportFactory( - sourceContext, + channelCredentials, mainThreadExecutor, schedulerPool, managedChannelImplBuilder.getOffloadExecutorPool(), securityPolicy, bindServiceFlags, inboundParcelablePolicy, - channelCredentials, targetUserHandle); } } @@ -325,36 +319,35 @@ public BinderChannelBuilder idleTimeout(long value, TimeUnit unit) { /** Creates new binder transports. */ private static final class TransportFactory implements ClientTransportFactory { - private final Context sourceContext; + private final BinderChannelCredentials channelCredentials; private final Executor mainThreadExecutor; private final ObjectPool scheduledExecutorPool; private final ObjectPool offloadExecutorPool; private final SecurityPolicy securityPolicy; private final InboundParcelablePolicy inboundParcelablePolicy; private final BindServiceFlags bindServiceFlags; + @Nullable private final UserHandle targetUserHandle; private ScheduledExecutorService executorService; private Executor offloadExecutor; private boolean closed; TransportFactory( - Context sourceContext, + BinderChannelCredentials channelCredentials, Executor mainThreadExecutor, ObjectPool scheduledExecutorPool, ObjectPool offloadExecutorPool, SecurityPolicy securityPolicy, BindServiceFlags bindServiceFlags, InboundParcelablePolicy inboundParcelablePolicy, - BinderChannelCredentials channelCredentials, @Nullable UserHandle targetUserHandle) { - this.sourceContext = sourceContext; + this.channelCredentials = channelCredentials; this.mainThreadExecutor = mainThreadExecutor; this.scheduledExecutorPool = scheduledExecutorPool; this.offloadExecutorPool = offloadExecutorPool; this.securityPolicy = securityPolicy; this.bindServiceFlags = bindServiceFlags; this.inboundParcelablePolicy = inboundParcelablePolicy; - this.channelCredentials = channelCredentials; this.targetUserHandle = targetUserHandle; executorService = scheduledExecutorPool.getObject(); @@ -368,7 +361,7 @@ public ConnectionClientTransport newClientTransport( throw new IllegalStateException("The transport factory is closed."); } return new BinderTransport.BinderClientTransport( - sourceContext, + channelCredentials, (AndroidComponentAddress) addr, bindServiceFlags, mainThreadExecutor, @@ -377,7 +370,6 @@ public ConnectionClientTransport newClientTransport( securityPolicy, inboundParcelablePolicy, options.getEagAttributes(), - channelCredentials, targetUserHandle); } diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java b/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java index a3b396a28d1..301e5419122 100644 --- a/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java +++ b/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java @@ -48,6 +48,10 @@ public ChannelCredentials withoutBearerTokens() { return this; } + public Context getSourceContext() { + return sourceContext; + } + @Nullable public ComponentName getDevicePolicyAdminComponentName() { return devicePolicyAdminComponentName; diff --git a/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java b/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java index 558cddec4c2..110e3031f6f 100644 --- a/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java +++ b/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java @@ -569,31 +569,7 @@ public static final class BinderClientTransport extends BinderTransport private int latestCallId = FIRST_CALL_ID; public BinderClientTransport( - Context sourceContext, - AndroidComponentAddress targetAddress, - BindServiceFlags bindServiceFlags, - Executor mainThreadExecutor, - ObjectPool executorServicePool, - ObjectPool offloadExecutorPool, - SecurityPolicy securityPolicy, - InboundParcelablePolicy inboundParcelablePolicy, - Attributes eagAttrs) { - this( - sourceContext, - targetAddress, - bindServiceFlags, - mainThreadExecutor, - executorServicePool, - offloadExecutorPool, - securityPolicy, - inboundParcelablePolicy, - eagAttrs, - BinderChannelCredentials.forDefault(sourceContext), - null); - } - - public BinderClientTransport( - Context sourceContext, + BinderChannelCredentials channelCredentials, AndroidComponentAddress targetAddress, BindServiceFlags bindServiceFlags, Executor mainThreadExecutor, @@ -602,12 +578,12 @@ public BinderClientTransport( SecurityPolicy securityPolicy, InboundParcelablePolicy inboundParcelablePolicy, Attributes eagAttrs, - BinderChannelCredentials channelCredentials, @Nullable UserHandle targetUserHandle) { super( executorServicePool, - buildClientAttributes(eagAttrs, sourceContext, targetAddress, inboundParcelablePolicy), - buildLogId(sourceContext, targetAddress)); + buildClientAttributes( + eagAttrs, channelCredentials.getSourceContext(), targetAddress, inboundParcelablePolicy), + buildLogId(channelCredentials.getSourceContext(), targetAddress)); this.offloadExecutorPool = offloadExecutorPool; this.securityPolicy = securityPolicy; this.offloadExecutor = offloadExecutorPool.getObject(); @@ -617,10 +593,9 @@ public BinderClientTransport( serviceBinding = new ServiceBinding( mainThreadExecutor, - sourceContext, + channelCredentials, targetAddress.asBindIntent(), bindServiceFlags.toInteger(), - channelCredentials, targetUserHandle, this); } diff --git a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java index 4ceab44d391..6caa9e8702f 100644 --- a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java +++ b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java @@ -82,10 +82,9 @@ private enum State { @AnyThread ServiceBinding( Executor mainThreadExecutor, - Context sourceContext, + BinderChannelCredentials channelCredentials, Intent bindIntent, int bindFlags, - BinderChannelCredentials channelCredentials, @Nullable UserHandle targetUserHandle, Observer observer) { // We need to synchronize here ensure other threads see all @@ -94,9 +93,9 @@ private enum State { this.bindIntent = bindIntent; this.bindFlags = bindFlags; this.observer = observer; - this.sourceContext = sourceContext; - this.mainThreadExecutor = mainThreadExecutor; this.channelCredentials = channelCredentials; + this.sourceContext = channelCredentials.getSourceContext(); + this.mainThreadExecutor = mainThreadExecutor; this.targetUserHandle = targetUserHandle; state = State.NOT_BINDING; reportedState = State.NOT_BINDING; @@ -130,9 +129,9 @@ public synchronized void bind() { state = State.BINDING; Status bindResult = bindInternal( - sourceContext, bindIntent, this, bindFlags, channelCredentials, targetUserHandle); + sourceContext, channelCredentials, bindIntent, this, bindFlags, targetUserHandle); if (!bindResult.isOk()) { - handleBindServiceFailure(sourceContext, this); + handleBindServiceFailure(channelCredentials.getSourceContext(), this); state = State.UNBOUND; mainThreadExecutor.execute(() -> notifyUnbound(bindResult)); } @@ -141,10 +140,10 @@ public synchronized void bind() { private static Status bindInternal( Context context, + BinderChannelCredentials channelCredentials, Intent bindIntent, ServiceConnection conn, int flags, - BinderChannelCredentials channelCredentials, @Nullable UserHandle targetUserHandle) { String methodName = "bindService"; try { @@ -161,7 +160,7 @@ private static Status bindInternal( if (channelCredentials.getDevicePolicyAdminComponentName() != null) { methodName = "DevicePolicyManager.bindDeviceAdminServiceAsUser"; DevicePolicyManager devicePolicyManager = - context.getSystemService(DevicePolicyManager.class); + (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); if (!devicePolicyManager.bindDeviceAdminServiceAsUser( channelCredentials.getDevicePolicyAdminComponentName(), bindIntent, diff --git a/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java b/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java index 9e2496087e2..99824e31793 100644 --- a/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java +++ b/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java @@ -29,12 +29,16 @@ import android.content.Context; import android.content.Intent; import android.os.IBinder; +import android.os.Parcel; +import android.os.UserHandle; import androidx.core.content.ContextCompat; import androidx.test.core.app.ApplicationProvider; import io.grpc.Status; import io.grpc.Status.Code; import io.grpc.binder.BinderChannelCredentials; import io.grpc.binder.internal.Bindable.Observer; +import java.util.Arrays; +import javax.annotation.Nullable; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -260,9 +264,10 @@ public void testCallsAfterUnbindDontCrash() throws Exception { } @Test + @Config(sdk = 30) public void testBindWithTargetUserHandle() throws Exception { binding = - newBuilder().setTargetUserHandle(UserHandle.getUserHandleForUid(/* userId= */ 0)).build(); + newBuilder().setTargetUserHandle(generateUserHandle(/* userId= */ 0)).build(); shadowOf(getMainLooper()).idle(); binding.bind(); @@ -276,6 +281,7 @@ public void testBindWithTargetUserHandle() throws Exception { } @Test + @Config(sdk = 30) public void testBindWithDeviceAdmin() throws Exception { String deviceAdminClassName = "DevicePolicyAdmin"; ComponentName adminComponent = new ComponentName(appContext, deviceAdminClassName); @@ -283,6 +289,7 @@ public void testBindWithDeviceAdmin() throws Exception { binding = newBuilder() .setTargetUserHandle(UserHandle.getUserHandleForUid(/* userId= */ 0)) + .setTargetUserHandle(generateUserHandle(/* userId= */ 0)) .setChannelCredentials( BinderChannelCredentials.forDevicePolicyAdmin(appContext, adminComponent)) .build(); @@ -316,6 +323,20 @@ private static void allowBindDeviceAdminForUser(Context context, ComponentName a devicePolicyManager.setDeviceOwner(admin); devicePolicyManager.setBindDeviceAdminTargetUsers( Arrays.asList(UserHandle.getUserHandleForUid(userId))); + shadowOf((DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE)); + devicePolicyManager.setDeviceOwner(admin); + devicePolicyManager.setBindDeviceAdminTargetUsers( + Arrays.asList(generateUserHandle(userId))); + } + + /** Generate UserHandles the hard way. */ + private static UserHandle generateUserHandle(int userId) { + Parcel userParcel = Parcel.obtain(); + userParcel.writeInt(userId); + userParcel.setDataPosition(0); + UserHandle userHandle = new UserHandle(userParcel); + userParcel.recycle(); + return userHandle; } private class TestObserver implements Bindable.Observer { @@ -344,7 +365,6 @@ public void onUnbound(Status reason) { } private static class ServiceBindingBuilder { - private Context sourceContext; private Observer observer; private Intent bindIntent = new Intent(); private int bindServiceFlags; @@ -390,8 +410,8 @@ public ServiceBindingBuilder setChannelCredentials( public ServiceBinding build() { return new ServiceBinding( - ContextCompat.getMainExecutor(sourceContext), - sourceContext, + ContextCompat.getMainExecutor(channelCredentials.getSourceContext()), + channelCredentials, bindIntent, bindServiceFlags, channelCredentials, From 43dc50e27badb4a1752606102fc3d52a54ee610b Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Thu, 11 May 2023 14:41:49 +0800 Subject: [PATCH 06/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server. --- .../test/java/io/grpc/binder/internal/ServiceBindingTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java b/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java index 99824e31793..160ddfdc130 100644 --- a/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java +++ b/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java @@ -372,7 +372,6 @@ private static class ServiceBindingBuilder { private BinderChannelCredentials channelCredentials; public ServiceBindingBuilder setSourceContext(Context sourceContext) { - this.sourceContext = sourceContext; this.channelCredentials = BinderChannelCredentials.forDefault(sourceContext); return this; } @@ -414,7 +413,6 @@ public ServiceBinding build() { channelCredentials, bindIntent, bindServiceFlags, - channelCredentials, targetUserHandle, observer); } From 9c602b6d3ed2149df657b1cbe63c57ce104d6bec Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Thu, 11 May 2023 14:41:49 +0800 Subject: [PATCH 07/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server. --- .../java/io/grpc/binder/BinderChannelBuilder.java | 4 ++-- .../io/grpc/binder/BinderChannelCredentials.java | 11 ++++++++++- .../io/grpc/binder/internal/BinderTransport.java | 4 ++-- .../io/grpc/binder/internal/ServiceBinding.java | 14 +++++--------- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java index 67e9080864f..97786152c69 100644 --- a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java +++ b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java @@ -364,13 +364,13 @@ public ConnectionClientTransport newClientTransport( channelCredentials, (AndroidComponentAddress) addr, bindServiceFlags, + targetUserHandle, mainThreadExecutor, scheduledExecutorPool, offloadExecutorPool, securityPolicy, inboundParcelablePolicy, - options.getEagAttributes(), - targetUserHandle); + options.getEagAttributes()); } @Override diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java b/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java index 301e5419122..2cc3fa3c7bb 100644 --- a/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java +++ b/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java @@ -3,6 +3,7 @@ import android.content.ComponentName; import android.content.Context; +import androidx.annotation.MainThread; import io.grpc.ChannelCredentials; import io.grpc.ExperimentalApi; import javax.annotation.Nullable; @@ -34,7 +35,9 @@ public static BinderChannelCredentials forDevicePolicyAdmin( return new BinderChannelCredentials(sourceContext, devicePolicyAdminComponentName); } - private final Context sourceContext; + // The sourceContext field is intentionally not guarded, since (aside from the constructor), + // it is only modified in the main thread. + @Nullable private Context sourceContext; // Only null in the unbound state. @Nullable private final ComponentName devicePolicyAdminComponentName; private BinderChannelCredentials( @@ -48,6 +51,7 @@ public ChannelCredentials withoutBearerTokens() { return this; } + @Nullable public Context getSourceContext() { return sourceContext; } @@ -56,4 +60,9 @@ public Context getSourceContext() { public ComponentName getDevicePolicyAdminComponentName() { return devicePolicyAdminComponentName; } + + @MainThread + public void clearReferences() { + sourceContext = null; + } } diff --git a/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java b/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java index 110e3031f6f..98a69e2cba6 100644 --- a/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java +++ b/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java @@ -572,13 +572,13 @@ public BinderClientTransport( BinderChannelCredentials channelCredentials, AndroidComponentAddress targetAddress, BindServiceFlags bindServiceFlags, + @Nullable UserHandle targetUserHandle, Executor mainThreadExecutor, ObjectPool executorServicePool, ObjectPool offloadExecutorPool, SecurityPolicy securityPolicy, InboundParcelablePolicy inboundParcelablePolicy, - Attributes eagAttrs, - @Nullable UserHandle targetUserHandle) { + Attributes eagAttrs) { super( executorServicePool, buildClientAttributes( diff --git a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java index 6caa9e8702f..adf888d2f42 100644 --- a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java +++ b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java @@ -75,8 +75,6 @@ private enum State { // they're only modified in the main thread. The constructor contains a synchronized block // to ensure there's a write barrier when these fields are first written. - @Nullable private Context sourceContext; // Only null in the unbound state. - private State reportedState; // Only used on the main thread. @AnyThread @@ -94,7 +92,6 @@ private enum State { this.bindFlags = bindFlags; this.observer = observer; this.channelCredentials = channelCredentials; - this.sourceContext = channelCredentials.getSourceContext(); this.mainThreadExecutor = mainThreadExecutor; this.targetUserHandle = targetUserHandle; state = State.NOT_BINDING; @@ -128,8 +125,7 @@ public synchronized void bind() { if (state == State.NOT_BINDING) { state = State.BINDING; Status bindResult = - bindInternal( - sourceContext, channelCredentials, bindIntent, this, bindFlags, targetUserHandle); + bindInternal(channelCredentials, bindIntent, this, bindFlags, targetUserHandle); if (!bindResult.isOk()) { handleBindServiceFailure(channelCredentials.getSourceContext(), this); state = State.UNBOUND; @@ -139,12 +135,12 @@ public synchronized void bind() { } private static Status bindInternal( - Context context, BinderChannelCredentials channelCredentials, Intent bindIntent, ServiceConnection conn, int flags, @Nullable UserHandle targetUserHandle) { + Context context = channelCredentials.getSourceContext(); String methodName = "bindService"; try { if (targetUserHandle == null) { @@ -214,7 +210,7 @@ void unbindInternal(Status reason) { Context unbindFrom = null; synchronized (this) { if (state == State.BINDING || state == State.BOUND) { - unbindFrom = sourceContext; + unbindFrom = channelCredentials.getSourceContext(); } state = State.UNBOUND; } @@ -226,7 +222,7 @@ void unbindInternal(Status reason) { @MainThread private void clearReferences() { - sourceContext = null; + channelCredentials.clearReferences(); } @Override @@ -266,6 +262,6 @@ public void onBindingDied(ComponentName name) { @VisibleForTesting synchronized boolean isSourceContextCleared() { - return sourceContext == null; + return channelCredentials.getSourceContext() == null; } } From 718ef66c4b3f7ce3a0ca605663f905aaa5863fe1 Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Thu, 11 May 2023 14:41:49 +0800 Subject: [PATCH 08/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server. --- .../grpc/binder/internal/ServiceBinding.java | 7 ++-- .../binder/BinderChannelCredentialsTest.java | 32 +++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 binder/src/test/java/io/grpc/binder/BinderChannelCredentialsTest.java diff --git a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java index adf888d2f42..cff94397977 100644 --- a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java +++ b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java @@ -65,7 +65,6 @@ private enum State { private final int bindFlags; private final Observer observer; private final Executor mainThreadExecutor; - private final BinderChannelCredentials channelCredentials; @Nullable private final UserHandle targetUserHandle; @GuardedBy("this") @@ -75,6 +74,8 @@ private enum State { // they're only modified in the main thread. The constructor contains a synchronized block // to ensure there's a write barrier when these fields are first written. + @Nullable private BinderChannelCredentials channelCredentials; // Only null in the unbound state. + private State reportedState; // Only used on the main thread. @AnyThread @@ -222,7 +223,7 @@ void unbindInternal(Status reason) { @MainThread private void clearReferences() { - channelCredentials.clearReferences(); + channelCredentials = null; } @Override @@ -262,6 +263,6 @@ public void onBindingDied(ComponentName name) { @VisibleForTesting synchronized boolean isSourceContextCleared() { - return channelCredentials.getSourceContext() == null; + return channelCredentials == null; } } diff --git a/binder/src/test/java/io/grpc/binder/BinderChannelCredentialsTest.java b/binder/src/test/java/io/grpc/binder/BinderChannelCredentialsTest.java new file mode 100644 index 00000000000..273607cd108 --- /dev/null +++ b/binder/src/test/java/io/grpc/binder/BinderChannelCredentialsTest.java @@ -0,0 +1,32 @@ +package io.grpc.binder; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.ComponentName; +import android.content.Context; +import androidx.test.core.app.ApplicationProvider; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class BinderChannelCredentialsTest { + private final Context appContext = ApplicationProvider.getApplicationContext(); + + @Test + public void defaultBinderChannelCredentials() { + BinderChannelCredentials channelCredentials = BinderChannelCredentials.forDefault(appContext); + assertThat(channelCredentials.getDevicePolicyAdminComponentName()).isNull(); + } + + @Test + public void binderChannelCredentialsForDevicePolicyAdmin() { + String deviceAdminClassName = "DevicePolicyAdmin"; + BinderChannelCredentials channelCredentials = + BinderChannelCredentials.forDevicePolicyAdmin( + appContext, new ComponentName(appContext, deviceAdminClassName)); + assertThat(channelCredentials.getDevicePolicyAdminComponentName()).isNotNull(); + assertThat(channelCredentials.getDevicePolicyAdminComponentName().getClassName()) + .isEqualTo(deviceAdminClassName); + } +} From 9bdf9926b00485c80419ebac7efa1ee882de9d4f Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Thu, 11 May 2023 14:41:49 +0800 Subject: [PATCH 09/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server. --- .../grpc/binder/internal/ServiceBinding.java | 72 ++++++++++++------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java index cff94397977..5a1ad8cf562 100644 --- a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java +++ b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java @@ -61,6 +61,23 @@ private enum State { UNBOUND, } + // Type of the method used when binding the service. + private enum BindMethodType { + BIND_SERVICE("bindService"), + BIND_SERVICE_AS_USER("bindServiceAsUser"), + DEVICE_POLICY_BIND_SEVICE_ADMIN("DevicePolicyManager.bindDeviceAdminServiceAsUser"); + + private final String methodName; + + BindMethodType(String methodName) { + this.methodName = methodName; + } + + public String methodName() { + return methodName; + } + } + private final Intent bindIntent; private final int bindFlags; private final Observer observer; @@ -142,48 +159,51 @@ private static Status bindInternal( int flags, @Nullable UserHandle targetUserHandle) { Context context = channelCredentials.getSourceContext(); - String methodName = "bindService"; + BindMethodType bindMethodType = BindMethodType.BIND_SERVICE; try { if (targetUserHandle == null) { checkState( channelCredentials.getDevicePolicyAdminComponentName() == null, "BindingChannelCredentials is expected to have null devicePolicyAdmin when" + " targetUserHandle is not set"); - if (!context.bindService(bindIntent, conn, flags)) { - return Status.UNIMPLEMENTED.withDescription( - "bindService(" + bindIntent + ") returned false"); - } } else { if (channelCredentials.getDevicePolicyAdminComponentName() != null) { - methodName = "DevicePolicyManager.bindDeviceAdminServiceAsUser"; - DevicePolicyManager devicePolicyManager = - (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); - if (!devicePolicyManager.bindDeviceAdminServiceAsUser( - channelCredentials.getDevicePolicyAdminComponentName(), - bindIntent, - conn, - flags, - targetUserHandle)) { - return Status.UNIMPLEMENTED.withDescription( - "DevicePolicyManager.bindDeviceAdminServiceAsUser(" - + bindIntent - + ") returned false"); - } + bindMethodType = BindMethodType.DEVICE_POLICY_BIND_SEVICE_ADMIN; } else { - methodName = "bindServiceAsUser"; - if (!context.bindServiceAsUser(bindIntent, conn, flags, targetUserHandle)) { - return Status.UNIMPLEMENTED.withDescription( - "bindServiceAsUser(" + bindIntent + ") returned false"); - } + bindMethodType = BindMethodType.BIND_SERVICE_AS_USER; } } + boolean bindResult = false; + switch (bindMethodType) { + case BIND_SERVICE: + bindResult = context.bindService(bindIntent, conn, flags); + break; + case BIND_SERVICE_AS_USER: + bindResult = context.bindServiceAsUser(bindIntent, conn, flags, targetUserHandle); + break; + case DEVICE_POLICY_BIND_SEVICE_ADMIN: + DevicePolicyManager devicePolicyManager = + (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); + bindResult = devicePolicyManager.bindDeviceAdminServiceAsUser( + channelCredentials.getDevicePolicyAdminComponentName(), + bindIntent, + conn, + flags, + targetUserHandle); + break; + } + if (!bindResult) { + return Status.UNIMPLEMENTED.withDescription( + bindMethodType.methodName() + "(" + bindIntent + ") returned false"); + } return Status.OK; } catch (SecurityException e) { return Status.PERMISSION_DENIED .withCause(e) - .withDescription("SecurityException from " + methodName); + .withDescription("SecurityException from " + bindMethodType.methodName()); } catch (RuntimeException e) { - return Status.INTERNAL.withCause(e).withDescription("RuntimeException from " + methodName); + return Status.INTERNAL.withCause(e).withDescription( + "RuntimeException from " + bindMethodType.methodName()); } } From 017fb7f3b46ced65d2a4a5d12faa0532fbe391bc Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Thu, 11 May 2023 14:41:49 +0800 Subject: [PATCH 10/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server. --- .../grpc/binder/BinderChannelCredentials.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java b/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java index 2cc3fa3c7bb..b84850ee72e 100644 --- a/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java +++ b/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java @@ -1,6 +1,23 @@ +/* + * Copyright 2022 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.grpc.binder; +import static com.google.common.base.Preconditions.checkNotNull; + import android.content.ComponentName; import android.content.Context; import androidx.annotation.MainThread; @@ -19,7 +36,7 @@ public final class BinderChannelCredentials extends ChannelCredentials { * @return a BinderChannelCredentials */ public static BinderChannelCredentials forDefault(Context sourceContext) { - return new BinderChannelCredentials(sourceContext, null); + return new BinderChannelCredentials(checkNotNull(sourceContext, "sourceContext"), null); } /** @@ -32,7 +49,8 @@ public static BinderChannelCredentials forDefault(Context sourceContext) { */ public static BinderChannelCredentials forDevicePolicyAdmin( Context sourceContext, ComponentName devicePolicyAdminComponentName) { - return new BinderChannelCredentials(sourceContext, devicePolicyAdminComponentName); + return new BinderChannelCredentials( + checkNotNull(sourceContext, "sourceContext"), devicePolicyAdminComponentName); } // The sourceContext field is intentionally not guarded, since (aside from the constructor), From 14b243cc61e9fef7bd3d29806cc5041e6ec053e6 Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Thu, 11 May 2023 14:41:49 +0800 Subject: [PATCH 11/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server. --- .../io/grpc/binder/BinderChannelBuilder.java | 40 +++++++++++-------- .../grpc/binder/BinderChannelCredentials.java | 31 +++++--------- .../grpc/binder/internal/BinderTransport.java | 11 ++--- .../grpc/binder/internal/ServiceBinding.java | 29 +++++++++----- .../binder/BinderChannelCredentialsTest.java | 4 +- .../binder/internal/ServiceBindingTest.java | 12 +++--- 6 files changed, 69 insertions(+), 58 deletions(-) diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java index 97786152c69..731bbbf8f9d 100644 --- a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java +++ b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java @@ -74,7 +74,8 @@ public static BinderChannelBuilder forAddress( return new BinderChannelBuilder( checkNotNull(directAddress, "directAddress"), null, - BinderChannelCredentials.forDefault(sourceContext)); + sourceContext, + BinderChannelCredentials.forDefault()); } /** @@ -90,16 +91,17 @@ public static BinderChannelBuilder forAddress( * resulting builder. They will not be shut down automatically. * * @param directAddress the {@link AndroidComponentAddress} referencing the service to bind to. + * @param sourceContext the context to bind from (e.g. The current Activity or Application). * @param channelCredentials the arbitrary binder specific channel credentials to be used to - * establish a binder connection including the context to bind from (e.g. The current - * Activity or Application). + * establish a binder connection. * @return a new builder */ public static BinderChannelBuilder forAddress( AndroidComponentAddress directAddress, + Context sourceContext, BinderChannelCredentials channelCredentials) { return new BinderChannelBuilder( - checkNotNull(directAddress, "directAddress"), null, channelCredentials); + checkNotNull(directAddress, "directAddress"), null, sourceContext, channelCredentials); } /** @@ -123,7 +125,8 @@ public static BinderChannelBuilder forTarget(String target, Context sourceContex return new BinderChannelBuilder( null, checkNotNull(target, "target"), - BinderChannelCredentials.forDefault(sourceContext)); + sourceContext, + BinderChannelCredentials.forDefault()); } /** @@ -140,15 +143,15 @@ public static BinderChannelBuilder forTarget(String target, Context sourceContex * * @param target A target uri which should resolve into an {@link AndroidComponentAddress} * referencing the service to bind to. + * @param sourceContext the context to bind from (e.g. The current Activity or Application). * @param channelCredentials the arbitrary binder specific channel credentials to be used to - * establish a binder connection including the context to bind from (e.g. The current - * Activity or Application). + * establish a binder connection. * @return a new builder */ public static BinderChannelBuilder forTarget( - String target, BinderChannelCredentials channelCredentials) { + String target, Context sourceContext, BinderChannelCredentials channelCredentials) { return new BinderChannelBuilder( - null, checkNotNull(target, "target"), channelCredentials); + null, checkNotNull(target, "target"), sourceContext, channelCredentials); } /** @@ -183,10 +186,10 @@ public static BinderChannelBuilder forTarget(String target) { private BinderChannelBuilder( @Nullable AndroidComponentAddress directAddress, @Nullable String target, + Context sourceContext, BinderChannelCredentials channelCredentials) { mainThreadExecutor = - ContextCompat.getMainExecutor( - checkNotNull(channelCredentials.getSourceContext(), "sourceContext")); + ContextCompat.getMainExecutor(checkNotNull(sourceContext, "sourceContext")); securityPolicy = SecurityPolicies.internalOnly(); inboundParcelablePolicy = InboundParcelablePolicy.DEFAULT; bindServiceFlags = BindServiceFlags.DEFAULTS; @@ -196,13 +199,14 @@ final class BinderChannelTransportFactoryBuilder @Override public ClientTransportFactory buildClientTransportFactory() { return new TransportFactory( - channelCredentials, + sourceContext, mainThreadExecutor, schedulerPool, managedChannelImplBuilder.getOffloadExecutorPool(), securityPolicy, bindServiceFlags, inboundParcelablePolicy, + channelCredentials, targetUserHandle); } } @@ -319,13 +323,14 @@ public BinderChannelBuilder idleTimeout(long value, TimeUnit unit) { /** Creates new binder transports. */ private static final class TransportFactory implements ClientTransportFactory { - private final BinderChannelCredentials channelCredentials; + private final Context sourceContext; private final Executor mainThreadExecutor; private final ObjectPool scheduledExecutorPool; private final ObjectPool offloadExecutorPool; private final SecurityPolicy securityPolicy; private final InboundParcelablePolicy inboundParcelablePolicy; private final BindServiceFlags bindServiceFlags; + private final BinderChannelCredentials channelCredentials; @Nullable private final UserHandle targetUserHandle; private ScheduledExecutorService executorService; @@ -333,21 +338,23 @@ private static final class TransportFactory implements ClientTransportFactory { private boolean closed; TransportFactory( - BinderChannelCredentials channelCredentials, + Context sourceContext, Executor mainThreadExecutor, ObjectPool scheduledExecutorPool, ObjectPool offloadExecutorPool, SecurityPolicy securityPolicy, BindServiceFlags bindServiceFlags, InboundParcelablePolicy inboundParcelablePolicy, + BinderChannelCredentials channelCredentials, @Nullable UserHandle targetUserHandle) { - this.channelCredentials = channelCredentials; + this.sourceContext = sourceContext; this.mainThreadExecutor = mainThreadExecutor; this.scheduledExecutorPool = scheduledExecutorPool; this.offloadExecutorPool = offloadExecutorPool; this.securityPolicy = securityPolicy; this.bindServiceFlags = bindServiceFlags; this.inboundParcelablePolicy = inboundParcelablePolicy; + this.channelCredentials = channelCredentials; this.targetUserHandle = targetUserHandle; executorService = scheduledExecutorPool.getObject(); @@ -361,9 +368,10 @@ public ConnectionClientTransport newClientTransport( throw new IllegalStateException("The transport factory is closed."); } return new BinderTransport.BinderClientTransport( - channelCredentials, + sourceContext, (AndroidComponentAddress) addr, bindServiceFlags, + channelCredentials, targetUserHandle, mainThreadExecutor, scheduledExecutorPool, diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java b/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java index b84850ee72e..c7c07c4e3e0 100644 --- a/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java +++ b/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java @@ -19,8 +19,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import android.content.ComponentName; -import android.content.Context; -import androidx.annotation.MainThread; import io.grpc.ChannelCredentials; import io.grpc.ExperimentalApi; import javax.annotation.Nullable; @@ -32,35 +30,38 @@ public final class BinderChannelCredentials extends ChannelCredentials { /** * Creates the default BinderChannelCredentials. * - * @param sourceContext the context to bind from (e.g. The current Activity or Application). * @return a BinderChannelCredentials */ +<<<<<<< HEAD public static BinderChannelCredentials forDefault(Context sourceContext) { return new BinderChannelCredentials(checkNotNull(sourceContext, "sourceContext"), null); +======= + public static BinderChannelCredentials forDefault() { + return new BinderChannelCredentials(null); +>>>>>>> 2b7f83473 (Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server.) } /** * Creates a BinderChannelCredentials to be used with DevicePolicyManager API. * - * @param sourceContext the context to bind from (e.g. The current Activity or Application). * @param devicePolicyAdminComponentName the admin component to be specified with * DevicePolicyManager.bindDeviceAdminServiceAsUser API. * @return a BinderChannelCredentials */ public static BinderChannelCredentials forDevicePolicyAdmin( +<<<<<<< HEAD Context sourceContext, ComponentName devicePolicyAdminComponentName) { return new BinderChannelCredentials( checkNotNull(sourceContext, "sourceContext"), devicePolicyAdminComponentName); +======= + ComponentName devicePolicyAdminComponentName) { + return new BinderChannelCredentials(devicePolicyAdminComponentName); +>>>>>>> 2b7f83473 (Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server.) } - // The sourceContext field is intentionally not guarded, since (aside from the constructor), - // it is only modified in the main thread. - @Nullable private Context sourceContext; // Only null in the unbound state. @Nullable private final ComponentName devicePolicyAdminComponentName; - private BinderChannelCredentials( - Context sourceContext, @Nullable ComponentName devicePolicyAdminComponentName) { - this.sourceContext = sourceContext; + private BinderChannelCredentials(@Nullable ComponentName devicePolicyAdminComponentName) { this.devicePolicyAdminComponentName = devicePolicyAdminComponentName; } @@ -69,18 +70,8 @@ public ChannelCredentials withoutBearerTokens() { return this; } - @Nullable - public Context getSourceContext() { - return sourceContext; - } - @Nullable public ComponentName getDevicePolicyAdminComponentName() { return devicePolicyAdminComponentName; } - - @MainThread - public void clearReferences() { - sourceContext = null; - } } diff --git a/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java b/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java index 98a69e2cba6..64b1430f540 100644 --- a/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java +++ b/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java @@ -569,9 +569,10 @@ public static final class BinderClientTransport extends BinderTransport private int latestCallId = FIRST_CALL_ID; public BinderClientTransport( - BinderChannelCredentials channelCredentials, + Context sourceContext, AndroidComponentAddress targetAddress, BindServiceFlags bindServiceFlags, + BinderChannelCredentials channelCredentials, @Nullable UserHandle targetUserHandle, Executor mainThreadExecutor, ObjectPool executorServicePool, @@ -581,9 +582,8 @@ public BinderClientTransport( Attributes eagAttrs) { super( executorServicePool, - buildClientAttributes( - eagAttrs, channelCredentials.getSourceContext(), targetAddress, inboundParcelablePolicy), - buildLogId(channelCredentials.getSourceContext(), targetAddress)); + buildClientAttributes(eagAttrs, sourceContext, targetAddress, inboundParcelablePolicy), + buildLogId(sourceContext, targetAddress)); this.offloadExecutorPool = offloadExecutorPool; this.securityPolicy = securityPolicy; this.offloadExecutor = offloadExecutorPool.getObject(); @@ -593,9 +593,10 @@ public BinderClientTransport( serviceBinding = new ServiceBinding( mainThreadExecutor, - channelCredentials, + sourceContext, targetAddress.asBindIntent(), bindServiceFlags.toInteger(), + channelCredentials, targetUserHandle, this); } diff --git a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java index 5a1ad8cf562..5063b057540 100644 --- a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java +++ b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java @@ -80,6 +80,7 @@ public String methodName() { private final Intent bindIntent; private final int bindFlags; + private final BinderChannelCredentials channelCredentials; private final Observer observer; private final Executor mainThreadExecutor; @Nullable private final UserHandle targetUserHandle; @@ -91,16 +92,17 @@ public String methodName() { // they're only modified in the main thread. The constructor contains a synchronized block // to ensure there's a write barrier when these fields are first written. - @Nullable private BinderChannelCredentials channelCredentials; // Only null in the unbound state. + @Nullable private Context sourceContext; // Only null in the unbound state. private State reportedState; // Only used on the main thread. @AnyThread ServiceBinding( Executor mainThreadExecutor, - BinderChannelCredentials channelCredentials, + Context sourceContext, Intent bindIntent, int bindFlags, + BinderChannelCredentials channelCredentials, @Nullable UserHandle targetUserHandle, Observer observer) { // We need to synchronize here ensure other threads see all @@ -109,8 +111,9 @@ public String methodName() { this.bindIntent = bindIntent; this.bindFlags = bindFlags; this.observer = observer; - this.channelCredentials = channelCredentials; + this.sourceContext = sourceContext; this.mainThreadExecutor = mainThreadExecutor; + this.channelCredentials = channelCredentials; this.targetUserHandle = targetUserHandle; state = State.NOT_BINDING; reportedState = State.NOT_BINDING; @@ -143,9 +146,15 @@ public synchronized void bind() { if (state == State.NOT_BINDING) { state = State.BINDING; Status bindResult = - bindInternal(channelCredentials, bindIntent, this, bindFlags, targetUserHandle); + bindInternal( + sourceContext, + bindIntent, + this, + bindFlags, + channelCredentials, + targetUserHandle); if (!bindResult.isOk()) { - handleBindServiceFailure(channelCredentials.getSourceContext(), this); + handleBindServiceFailure(sourceContext, this); state = State.UNBOUND; mainThreadExecutor.execute(() -> notifyUnbound(bindResult)); } @@ -153,12 +162,12 @@ public synchronized void bind() { } private static Status bindInternal( - BinderChannelCredentials channelCredentials, + Context context, Intent bindIntent, ServiceConnection conn, int flags, + BinderChannelCredentials channelCredentials, @Nullable UserHandle targetUserHandle) { - Context context = channelCredentials.getSourceContext(); BindMethodType bindMethodType = BindMethodType.BIND_SERVICE; try { if (targetUserHandle == null) { @@ -231,7 +240,7 @@ void unbindInternal(Status reason) { Context unbindFrom = null; synchronized (this) { if (state == State.BINDING || state == State.BOUND) { - unbindFrom = channelCredentials.getSourceContext(); + unbindFrom = sourceContext; } state = State.UNBOUND; } @@ -243,7 +252,7 @@ void unbindInternal(Status reason) { @MainThread private void clearReferences() { - channelCredentials = null; + sourceContext = null; } @Override @@ -283,6 +292,6 @@ public void onBindingDied(ComponentName name) { @VisibleForTesting synchronized boolean isSourceContextCleared() { - return channelCredentials == null; + return sourceContext == null; } } diff --git a/binder/src/test/java/io/grpc/binder/BinderChannelCredentialsTest.java b/binder/src/test/java/io/grpc/binder/BinderChannelCredentialsTest.java index 273607cd108..d31065dfe52 100644 --- a/binder/src/test/java/io/grpc/binder/BinderChannelCredentialsTest.java +++ b/binder/src/test/java/io/grpc/binder/BinderChannelCredentialsTest.java @@ -15,7 +15,7 @@ public class BinderChannelCredentialsTest { @Test public void defaultBinderChannelCredentials() { - BinderChannelCredentials channelCredentials = BinderChannelCredentials.forDefault(appContext); + BinderChannelCredentials channelCredentials = BinderChannelCredentials.forDefault(); assertThat(channelCredentials.getDevicePolicyAdminComponentName()).isNull(); } @@ -24,7 +24,7 @@ public void binderChannelCredentialsForDevicePolicyAdmin() { String deviceAdminClassName = "DevicePolicyAdmin"; BinderChannelCredentials channelCredentials = BinderChannelCredentials.forDevicePolicyAdmin( - appContext, new ComponentName(appContext, deviceAdminClassName)); + new ComponentName(appContext, deviceAdminClassName)); assertThat(channelCredentials.getDevicePolicyAdminComponentName()).isNotNull(); assertThat(channelCredentials.getDevicePolicyAdminComponentName().getClassName()) .isEqualTo(deviceAdminClassName); diff --git a/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java b/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java index 160ddfdc130..dec72a9466d 100644 --- a/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java +++ b/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java @@ -291,7 +291,7 @@ public void testBindWithDeviceAdmin() throws Exception { .setTargetUserHandle(UserHandle.getUserHandleForUid(/* userId= */ 0)) .setTargetUserHandle(generateUserHandle(/* userId= */ 0)) .setChannelCredentials( - BinderChannelCredentials.forDevicePolicyAdmin(appContext, adminComponent)) + BinderChannelCredentials.forDevicePolicyAdmin(adminComponent)) .build(); shadowOf(getMainLooper()).idle(); @@ -365,14 +365,15 @@ public void onUnbound(Status reason) { } private static class ServiceBindingBuilder { + private Context sourceContext; private Observer observer; private Intent bindIntent = new Intent(); private int bindServiceFlags; @Nullable private UserHandle targetUserHandle = null; - private BinderChannelCredentials channelCredentials; + private BinderChannelCredentials channelCredentials = BinderChannelCredentials.forDefault(); public ServiceBindingBuilder setSourceContext(Context sourceContext) { - this.channelCredentials = BinderChannelCredentials.forDefault(sourceContext); + this.sourceContext = sourceContext; return this; } @@ -409,10 +410,11 @@ public ServiceBindingBuilder setChannelCredentials( public ServiceBinding build() { return new ServiceBinding( - ContextCompat.getMainExecutor(channelCredentials.getSourceContext()), - channelCredentials, + ContextCompat.getMainExecutor(sourceContext), + sourceContext, bindIntent, bindServiceFlags, + channelCredentials, targetUserHandle, observer); } From e6945df531218d1e7e0fbe7680de3779aa54842c Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Thu, 11 May 2023 14:41:49 +0800 Subject: [PATCH 12/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server. --- .../grpc/binder/BinderChannelCredentials.java | 77 ------------------- 1 file changed, 77 deletions(-) delete mode 100644 binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java b/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java deleted file mode 100644 index c7c07c4e3e0..00000000000 --- a/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2022 The gRPC Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.grpc.binder; - -import static com.google.common.base.Preconditions.checkNotNull; - -import android.content.ComponentName; -import io.grpc.ChannelCredentials; -import io.grpc.ExperimentalApi; -import javax.annotation.Nullable; - -/** Additional arbitrary arguments to establish a Android binder connection channel. */ -@ExperimentalApi("https://github.com/grpc/grpc-java/issues/10173") -public final class BinderChannelCredentials extends ChannelCredentials { - - /** - * Creates the default BinderChannelCredentials. - * - * @return a BinderChannelCredentials - */ -<<<<<<< HEAD - public static BinderChannelCredentials forDefault(Context sourceContext) { - return new BinderChannelCredentials(checkNotNull(sourceContext, "sourceContext"), null); -======= - public static BinderChannelCredentials forDefault() { - return new BinderChannelCredentials(null); ->>>>>>> 2b7f83473 (Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server.) - } - - /** - * Creates a BinderChannelCredentials to be used with DevicePolicyManager API. - * - * @param devicePolicyAdminComponentName the admin component to be specified with - * DevicePolicyManager.bindDeviceAdminServiceAsUser API. - * @return a BinderChannelCredentials - */ - public static BinderChannelCredentials forDevicePolicyAdmin( -<<<<<<< HEAD - Context sourceContext, ComponentName devicePolicyAdminComponentName) { - return new BinderChannelCredentials( - checkNotNull(sourceContext, "sourceContext"), devicePolicyAdminComponentName); -======= - ComponentName devicePolicyAdminComponentName) { - return new BinderChannelCredentials(devicePolicyAdminComponentName); ->>>>>>> 2b7f83473 (Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server.) - } - - @Nullable private final ComponentName devicePolicyAdminComponentName; - - private BinderChannelCredentials(@Nullable ComponentName devicePolicyAdminComponentName) { - this.devicePolicyAdminComponentName = devicePolicyAdminComponentName; - } - - @Override - public ChannelCredentials withoutBearerTokens() { - return this; - } - - @Nullable - public ComponentName getDevicePolicyAdminComponentName() { - return devicePolicyAdminComponentName; - } -} From 5d34fe9ec70d4ec3dde14de483080a1037170cda Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Thu, 11 May 2023 14:41:49 +0800 Subject: [PATCH 13/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user ondevice server. --- .../grpc/binder/BinderChannelCredentials.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java b/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java new file mode 100644 index 00000000000..5afee89cdec --- /dev/null +++ b/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java @@ -0,0 +1,66 @@ +/* + * Copyright 2022 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc.binder; + +import static com.google.common.base.Preconditions.checkNotNull; + +import android.content.ComponentName; +import io.grpc.ChannelCredentials; +import io.grpc.ExperimentalApi; +import javax.annotation.Nullable; + +/** Additional arbitrary arguments to establish a Android binder connection channel. */ +@ExperimentalApi("https://github.com/grpc/grpc-java/issues/10173") +public final class BinderChannelCredentials extends ChannelCredentials { + + /** + * Creates the default BinderChannelCredentials. + * + * @return a BinderChannelCredentials + */ + public static BinderChannelCredentials forDefault() { + return new BinderChannelCredentials(null); + } + + /** + * Creates a BinderChannelCredentials to be used with DevicePolicyManager API. + * + * @param devicePolicyAdminComponentName the admin component to be specified with + * DevicePolicyManager.bindDeviceAdminServiceAsUser API. + * @return a BinderChannelCredentials + */ + public static BinderChannelCredentials forDevicePolicyAdmin( + ComponentName devicePolicyAdminComponentName) { + return new BinderChannelCredentials(devicePolicyAdminComponentName); + } + + @Nullable private final ComponentName devicePolicyAdminComponentName; + + private BinderChannelCredentials(@Nullable ComponentName devicePolicyAdminComponentName) { + this.devicePolicyAdminComponentName = devicePolicyAdminComponentName; + } + + @Override + public ChannelCredentials withoutBearerTokens() { + return this; + } + + @Nullable + public ComponentName getDevicePolicyAdminComponentName() { + return devicePolicyAdminComponentName; + } +} From 8f1d74c27ba08ce48f863a14b06f0c9a7c14f554 Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Tue, 27 Jun 2023 18:13:58 +0800 Subject: [PATCH 14/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user communication through OnDeviceServer. --- .../io/grpc/binder/BinderChannelBuilder.java | 28 +++++++++---------- .../grpc/binder/internal/BinderTransport.java | 8 +++--- .../grpc/binder/internal/ServiceBinding.java | 8 +++--- .../binder/internal/ServiceBindingTest.java | 4 +-- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java index 731bbbf8f9d..bfb796092f0 100644 --- a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java +++ b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java @@ -200,14 +200,14 @@ final class BinderChannelTransportFactoryBuilder public ClientTransportFactory buildClientTransportFactory() { return new TransportFactory( sourceContext, + channelCredentials, mainThreadExecutor, schedulerPool, managedChannelImplBuilder.getOffloadExecutorPool(), securityPolicy, + targetUserHandle, bindServiceFlags, - inboundParcelablePolicy, - channelCredentials, - targetUserHandle); + inboundParcelablePolicy); } } @@ -290,7 +290,7 @@ public BinderChannelBuilder securityPolicy(SecurityPolicy securityPolicy) { * @param targetUserHandle the target user to bind into. * @return this */ - public BinderChannelBuilder bindForUser(UserHandle targetUserHandle) { + public BinderChannelBuilder bindAsUser(UserHandle targetUserHandle) { this.targetUserHandle = targetUserHandle; return this; } @@ -324,14 +324,14 @@ public BinderChannelBuilder idleTimeout(long value, TimeUnit unit) { /** Creates new binder transports. */ private static final class TransportFactory implements ClientTransportFactory { private final Context sourceContext; + private final BinderChannelCredentials channelCredentials; private final Executor mainThreadExecutor; private final ObjectPool scheduledExecutorPool; private final ObjectPool offloadExecutorPool; private final SecurityPolicy securityPolicy; - private final InboundParcelablePolicy inboundParcelablePolicy; - private final BindServiceFlags bindServiceFlags; - private final BinderChannelCredentials channelCredentials; @Nullable private final UserHandle targetUserHandle; + private final BindServiceFlags bindServiceFlags; + private final InboundParcelablePolicy inboundParcelablePolicy; private ScheduledExecutorService executorService; private Executor offloadExecutor; @@ -339,23 +339,23 @@ private static final class TransportFactory implements ClientTransportFactory { TransportFactory( Context sourceContext, + BinderChannelCredentials channelCredentials, Executor mainThreadExecutor, ObjectPool scheduledExecutorPool, ObjectPool offloadExecutorPool, SecurityPolicy securityPolicy, + @Nullable UserHandle targetUserHandle, BindServiceFlags bindServiceFlags, - InboundParcelablePolicy inboundParcelablePolicy, - BinderChannelCredentials channelCredentials, - @Nullable UserHandle targetUserHandle) { + InboundParcelablePolicy inboundParcelablePolicy) { this.sourceContext = sourceContext; + this.channelCredentials = channelCredentials; this.mainThreadExecutor = mainThreadExecutor; this.scheduledExecutorPool = scheduledExecutorPool; this.offloadExecutorPool = offloadExecutorPool; this.securityPolicy = securityPolicy; + this.targetUserHandle = targetUserHandle; this.bindServiceFlags = bindServiceFlags; this.inboundParcelablePolicy = inboundParcelablePolicy; - this.channelCredentials = channelCredentials; - this.targetUserHandle = targetUserHandle; executorService = scheduledExecutorPool.getObject(); offloadExecutor = offloadExecutorPool.getObject(); @@ -369,10 +369,10 @@ public ConnectionClientTransport newClientTransport( } return new BinderTransport.BinderClientTransport( sourceContext, - (AndroidComponentAddress) addr, - bindServiceFlags, channelCredentials, + (AndroidComponentAddress) addr, targetUserHandle, + bindServiceFlags, mainThreadExecutor, scheduledExecutorPool, offloadExecutorPool, diff --git a/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java b/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java index 64b1430f540..cb2fe5be3e5 100644 --- a/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java +++ b/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java @@ -570,10 +570,10 @@ public static final class BinderClientTransport extends BinderTransport public BinderClientTransport( Context sourceContext, - AndroidComponentAddress targetAddress, - BindServiceFlags bindServiceFlags, BinderChannelCredentials channelCredentials, + AndroidComponentAddress targetAddress, @Nullable UserHandle targetUserHandle, + BindServiceFlags bindServiceFlags, Executor mainThreadExecutor, ObjectPool executorServicePool, ObjectPool offloadExecutorPool, @@ -594,10 +594,10 @@ public BinderClientTransport( new ServiceBinding( mainThreadExecutor, sourceContext, - targetAddress.asBindIntent(), - bindServiceFlags.toInteger(), channelCredentials, + targetAddress.asBindIntent(), targetUserHandle, + bindServiceFlags.toInteger(), this); } diff --git a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java index 5063b057540..32d0e7a4add 100644 --- a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java +++ b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java @@ -78,12 +78,12 @@ public String methodName() { } } + private final BinderChannelCredentials channelCredentials; private final Intent bindIntent; + @Nullable private final UserHandle targetUserHandle; private final int bindFlags; - private final BinderChannelCredentials channelCredentials; private final Observer observer; private final Executor mainThreadExecutor; - @Nullable private final UserHandle targetUserHandle; @GuardedBy("this") private State state; @@ -100,10 +100,10 @@ public String methodName() { ServiceBinding( Executor mainThreadExecutor, Context sourceContext, - Intent bindIntent, - int bindFlags, BinderChannelCredentials channelCredentials, + Intent bindIntent, @Nullable UserHandle targetUserHandle, + int bindFlags, Observer observer) { // We need to synchronize here ensure other threads see all // non-final fields initialized after the constructor. diff --git a/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java b/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java index dec72a9466d..3ec65624fb8 100644 --- a/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java +++ b/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java @@ -412,10 +412,10 @@ public ServiceBinding build() { return new ServiceBinding( ContextCompat.getMainExecutor(sourceContext), sourceContext, - bindIntent, - bindServiceFlags, channelCredentials, + bindIntent, targetUserHandle, + bindServiceFlags, observer); } } From f1ffd78903777d5c6a90458310d0738f604c42be Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Wed, 28 Jun 2023 10:52:18 +0800 Subject: [PATCH 15/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user communication through OnDeviceServer. --- .../main/java/io/grpc/binder/BinderChannelCredentials.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java b/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java index 5afee89cdec..1fa2136e4a5 100644 --- a/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java +++ b/binder/src/main/java/io/grpc/binder/BinderChannelCredentials.java @@ -59,6 +59,10 @@ public ChannelCredentials withoutBearerTokens() { return this; } + /** + * Returns the admin component to be specified with DevicePolicyManager + * bindDeviceAdminServiceAsUser API. + */ @Nullable public ComponentName getDevicePolicyAdminComponentName() { return devicePolicyAdminComponentName; From 81663f23f49b8a198f07d7c3b339ae35382d1d70 Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Wed, 28 Jun 2023 20:22:02 +0800 Subject: [PATCH 16/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user communication through OnDeviceServer. --- .../io/grpc/binder/internal/BinderClientTransportTest.java | 3 +++ .../java/io/grpc/binder/internal/BinderTransportTest.java | 3 +++ binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java | 2 ++ 3 files changed, 8 insertions(+) diff --git a/binder/src/androidTest/java/io/grpc/binder/internal/BinderClientTransportTest.java b/binder/src/androidTest/java/io/grpc/binder/internal/BinderClientTransportTest.java index dbacf351780..44454fbf7f1 100644 --- a/binder/src/androidTest/java/io/grpc/binder/internal/BinderClientTransportTest.java +++ b/binder/src/androidTest/java/io/grpc/binder/internal/BinderClientTransportTest.java @@ -35,6 +35,7 @@ import io.grpc.Status.Code; import io.grpc.binder.AndroidComponentAddress; import io.grpc.binder.BindServiceFlags; +import io.grpc.binder.BinderChannelCredentials; import io.grpc.binder.BinderServerBuilder; import io.grpc.binder.HostServices; import io.grpc.binder.InboundParcelablePolicy; @@ -146,7 +147,9 @@ public BinderClientTransportBuilder setSecurityPolicy(SecurityPolicy securityPol public BinderTransport.BinderClientTransport build() { return new BinderTransport.BinderClientTransport( appContext, + BinderChannelCredentials.forDefault(), serverAddress, + null, BindServiceFlags.DEFAULTS, ContextCompat.getMainExecutor(appContext), executorServicePool, diff --git a/binder/src/androidTest/java/io/grpc/binder/internal/BinderTransportTest.java b/binder/src/androidTest/java/io/grpc/binder/internal/BinderTransportTest.java index 5a1b302f768..5140057d72e 100644 --- a/binder/src/androidTest/java/io/grpc/binder/internal/BinderTransportTest.java +++ b/binder/src/androidTest/java/io/grpc/binder/internal/BinderTransportTest.java @@ -24,6 +24,7 @@ import io.grpc.ServerStreamTracer; import io.grpc.binder.AndroidComponentAddress; import io.grpc.binder.BindServiceFlags; +import io.grpc.binder.BinderChannelCredentials; import io.grpc.binder.HostServices; import io.grpc.binder.InboundParcelablePolicy; import io.grpc.binder.SecurityPolicies; @@ -95,7 +96,9 @@ protected ManagedClientTransport newClientTransport(InternalServer server) { AndroidComponentAddress addr = (AndroidComponentAddress) server.getListenSocketAddress(); return new BinderTransport.BinderClientTransport( appContext, + BinderChannelCredentials.forDefault(), addr, + null, BindServiceFlags.DEFAULTS, ContextCompat.getMainExecutor(appContext), executorServicePool, diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java index bfb796092f0..5a0cf969941 100644 --- a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java +++ b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java @@ -96,6 +96,7 @@ public static BinderChannelBuilder forAddress( * establish a binder connection. * @return a new builder */ + @ExperimentalApi("https://github.com/grpc/grpc-java/issues/10173") public static BinderChannelBuilder forAddress( AndroidComponentAddress directAddress, Context sourceContext, @@ -148,6 +149,7 @@ public static BinderChannelBuilder forTarget(String target, Context sourceContex * establish a binder connection. * @return a new builder */ + @ExperimentalApi("https://github.com/grpc/grpc-java/issues/10173") public static BinderChannelBuilder forTarget( String target, Context sourceContext, BinderChannelCredentials channelCredentials) { return new BinderChannelBuilder( From 6a3374638d9975314703669a3c1c368f49cbf8a9 Mon Sep 17 00:00:00 2001 From: wwtbuaa01 Date: Thu, 29 Jun 2023 11:35:33 +0800 Subject: [PATCH 17/17] Add UserHandle and BinderChannelCredentials to BinderChannelBuilder to support cross-user communication through OnDeviceServer. --- binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java | 1 + 1 file changed, 1 insertion(+) diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java index 5a0cf969941..e714761312a 100644 --- a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java +++ b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java @@ -292,6 +292,7 @@ public BinderChannelBuilder securityPolicy(SecurityPolicy securityPolicy) { * @param targetUserHandle the target user to bind into. * @return this */ + @ExperimentalApi("https://github.com/grpc/grpc-java/issues/10173") public BinderChannelBuilder bindAsUser(UserHandle targetUserHandle) { this.targetUserHandle = targetUserHandle; return this;