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 7710924d8c..f4775c79ab 100644 --- a/binder/src/androidTest/java/io/grpc/binder/internal/BinderClientTransportTest.java +++ b/binder/src/androidTest/java/io/grpc/binder/internal/BinderClientTransportTest.java @@ -47,6 +47,7 @@ import io.grpc.binder.internal.OneWayBinderProxies.BlockingBinderDecorator; import io.grpc.binder.internal.OneWayBinderProxies.ThrowingOneWayBinderProxy; import io.grpc.internal.ClientStream; import io.grpc.internal.ClientStreamListener; +import io.grpc.internal.ClientTransportFactory.ClientTransportOptions; import io.grpc.internal.FixedObjectPool; import io.grpc.internal.ManagedClientTransport; import io.grpc.internal.ObjectPool; @@ -142,34 +143,25 @@ public final class BinderClientTransportTest { } private class BinderClientTransportBuilder { - private SecurityPolicy securityPolicy = SecurityPolicies.internalOnly(); - private OneWayBinderProxy.Decorator binderDecorator = OneWayBinderProxy.IDENTITY_DECORATOR; + final BinderClientTransportFactory.Builder factoryBuilder = new BinderClientTransportFactory.Builder() + .setSourceContext(appContext) + .setScheduledExecutorPool(executorServicePool) + .setOffloadExecutorPool(executorServicePool); public BinderClientTransportBuilder setSecurityPolicy(SecurityPolicy securityPolicy) { - this.securityPolicy = securityPolicy; + factoryBuilder.setSecurityPolicy(securityPolicy); return this; } public BinderClientTransportBuilder setBinderDecorator( OneWayBinderProxy.Decorator binderDecorator) { - this.binderDecorator = binderDecorator; + factoryBuilder.setBinderDecorator(binderDecorator); return this; } public BinderTransport.BinderClientTransport build() { - return new BinderTransport.BinderClientTransport( - appContext, - BinderChannelCredentials.forDefault(), - serverAddress, - null, - BindServiceFlags.DEFAULTS, - ContextCompat.getMainExecutor(appContext), - executorServicePool, - executorServicePool, - securityPolicy, - InboundParcelablePolicy.DEFAULT, - binderDecorator, - Attributes.EMPTY); + return factoryBuilder.buildClientTransportFactory() + .newClientTransport(serverAddress, new ClientTransportOptions(), null); } } 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 a7f91fae01..91e66c5226 100644 --- a/binder/src/androidTest/java/io/grpc/binder/internal/BinderTransportTest.java +++ b/binder/src/androidTest/java/io/grpc/binder/internal/BinderTransportTest.java @@ -29,6 +29,8 @@ import io.grpc.binder.HostServices; import io.grpc.binder.InboundParcelablePolicy; import io.grpc.binder.SecurityPolicies; import io.grpc.internal.AbstractTransportTest; +import io.grpc.internal.ClientTransportFactory; +import io.grpc.internal.ClientTransportFactory.ClientTransportOptions; import io.grpc.internal.GrpcUtil; import io.grpc.internal.InternalServer; import io.grpc.internal.ManagedClientTransport; @@ -97,19 +99,19 @@ public final class BinderTransportTest extends AbstractTransportTest { @Override protected ManagedClientTransport newClientTransport(InternalServer server) { AndroidComponentAddress addr = (AndroidComponentAddress) server.getListenSocketAddress(); + BinderClientTransportFactory.Builder builder = new BinderClientTransportFactory.Builder() + .setSourceContext(appContext) + .setScheduledExecutorPool(executorServicePool) + .setOffloadExecutorPool(offloadExecutorPool); + + ClientTransportOptions options = new ClientTransportOptions(); + options.setEagAttributes(eagAttrs()); + options.setChannelLogger(transportLogger()); + return new BinderTransport.BinderClientTransport( - appContext, - BinderChannelCredentials.forDefault(), + builder.buildClientTransportFactory(), addr, - null, - BindServiceFlags.DEFAULTS, - ContextCompat.getMainExecutor(appContext), - executorServicePool, - offloadExecutorPool, - SecurityPolicies.internalOnly(), - InboundParcelablePolicy.DEFAULT, - OneWayBinderProxy.IDENTITY_DECORATOR, - eagAttrs()); + options); } @Test diff --git a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java index 133c8c5dd1..67d3631b97 100644 --- a/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java +++ b/binder/src/main/java/io/grpc/binder/BinderChannelBuilder.java @@ -22,27 +22,14 @@ import static com.google.common.base.Preconditions.checkState; import android.content.Context; import android.os.UserHandle; import androidx.annotation.RequiresApi; -import androidx.core.content.ContextCompat; import com.google.errorprone.annotations.DoNotCall; -import io.grpc.ChannelCredentials; -import io.grpc.ChannelLogger; import io.grpc.ExperimentalApi; import io.grpc.ForwardingChannelBuilder; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; -import io.grpc.binder.internal.BinderTransport; -import io.grpc.binder.internal.OneWayBinderProxy; -import io.grpc.internal.ClientTransportFactory; -import io.grpc.internal.ConnectionClientTransport; +import io.grpc.binder.internal.BinderClientTransportFactory; import io.grpc.internal.FixedObjectPool; -import io.grpc.internal.GrpcUtil; import io.grpc.internal.ManagedChannelImplBuilder; -import io.grpc.internal.ManagedChannelImplBuilder.ClientTransportFactoryBuilder; -import io.grpc.internal.ObjectPool; -import io.grpc.internal.SharedResourcePool; -import java.net.SocketAddress; -import java.util.Collection; -import java.util.Collections; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -179,14 +166,8 @@ public final class BinderChannelBuilder } private final ManagedChannelImplBuilder managedChannelImplBuilder; + private final BinderClientTransportFactory.Builder transportFactoryBuilder; - private Executor mainThreadExecutor; - private ObjectPool schedulerPool = - SharedResourcePool.forResource(GrpcUtil.TIMER_SERVICE); - private SecurityPolicy securityPolicy; - private InboundParcelablePolicy inboundParcelablePolicy; - private BindServiceFlags bindServiceFlags; - @Nullable private UserHandle targetUserHandle; private boolean strictLifecycleManagement; private BinderChannelBuilder( @@ -194,41 +175,22 @@ public final class BinderChannelBuilder @Nullable String target, Context sourceContext, BinderChannelCredentials channelCredentials) { - mainThreadExecutor = - ContextCompat.getMainExecutor(checkNotNull(sourceContext, "sourceContext")); - securityPolicy = SecurityPolicies.internalOnly(); - inboundParcelablePolicy = InboundParcelablePolicy.DEFAULT; - bindServiceFlags = BindServiceFlags.DEFAULTS; - - final class BinderChannelTransportFactoryBuilder - implements ClientTransportFactoryBuilder { - @Override - public ClientTransportFactory buildClientTransportFactory() { - return new TransportFactory( - sourceContext, - channelCredentials, - mainThreadExecutor, - schedulerPool, - managedChannelImplBuilder.getOffloadExecutorPool(), - securityPolicy, - targetUserHandle, - bindServiceFlags, - inboundParcelablePolicy); - } - } + transportFactoryBuilder = new BinderClientTransportFactory.Builder() + .setSourceContext(sourceContext) + .setChannelCredentials(channelCredentials); if (directAddress != null) { managedChannelImplBuilder = new ManagedChannelImplBuilder( directAddress, directAddress.getAuthority(), - new BinderChannelTransportFactoryBuilder(), + transportFactoryBuilder, null); } else { managedChannelImplBuilder = new ManagedChannelImplBuilder( target, - new BinderChannelTransportFactoryBuilder(), + transportFactoryBuilder, null); } idleTimeout(60, TimeUnit.SECONDS); @@ -242,7 +204,7 @@ public final class BinderChannelBuilder /** Specifies certain optional aspects of the underlying Android Service binding. */ public BinderChannelBuilder setBindServiceFlags(BindServiceFlags bindServiceFlags) { - this.bindServiceFlags = bindServiceFlags; + transportFactoryBuilder.setBindServiceFlags(bindServiceFlags); return this; } @@ -256,8 +218,8 @@ public final class BinderChannelBuilder */ public BinderChannelBuilder scheduledExecutorService( ScheduledExecutorService scheduledExecutorService) { - schedulerPool = - new FixedObjectPool<>(checkNotNull(scheduledExecutorService, "scheduledExecutorService")); + transportFactoryBuilder.setScheduledExecutorPool( + new FixedObjectPool<>(checkNotNull(scheduledExecutorService, "scheduledExecutorService"))); return this; } @@ -269,7 +231,7 @@ public final class BinderChannelBuilder * @return this */ public BinderChannelBuilder mainThreadExecutor(Executor mainThreadExecutor) { - this.mainThreadExecutor = mainThreadExecutor; + transportFactoryBuilder.setMainThreadExecutor(mainThreadExecutor); return this; } @@ -282,7 +244,7 @@ public final class BinderChannelBuilder * @return this */ public BinderChannelBuilder securityPolicy(SecurityPolicy securityPolicy) { - this.securityPolicy = checkNotNull(securityPolicy, "securityPolicy"); + transportFactoryBuilder.setSecurityPolicy(securityPolicy); return this; } @@ -300,14 +262,14 @@ public final class BinderChannelBuilder @ExperimentalApi("https://github.com/grpc/grpc-java/issues/10173") @RequiresApi(30) public BinderChannelBuilder bindAsUser(UserHandle targetUserHandle) { - this.targetUserHandle = targetUserHandle; + transportFactoryBuilder.setTargetUserHandle(targetUserHandle); return this; } /** Sets the policy for inbound parcelable objects. */ public BinderChannelBuilder inboundParcelablePolicy( InboundParcelablePolicy inboundParcelablePolicy) { - this.inboundParcelablePolicy = checkNotNull(inboundParcelablePolicy, "inboundParcelablePolicy"); + transportFactoryBuilder.setInboundParcelablePolicy(inboundParcelablePolicy); return this; } @@ -330,87 +292,10 @@ public final class BinderChannelBuilder return this; } - /** 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; - @Nullable private final UserHandle targetUserHandle; - private final BindServiceFlags bindServiceFlags; - private final InboundParcelablePolicy inboundParcelablePolicy; - - private ScheduledExecutorService executorService; - private Executor offloadExecutor; - private boolean closed; - - TransportFactory( - Context sourceContext, - BinderChannelCredentials channelCredentials, - Executor mainThreadExecutor, - ObjectPool scheduledExecutorPool, - ObjectPool offloadExecutorPool, - SecurityPolicy securityPolicy, - @Nullable UserHandle targetUserHandle, - BindServiceFlags bindServiceFlags, - 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; - - executorService = scheduledExecutorPool.getObject(); - offloadExecutor = offloadExecutorPool.getObject(); - } - - @Override - public ConnectionClientTransport newClientTransport( - SocketAddress addr, ClientTransportOptions options, ChannelLogger channelLogger) { - if (closed) { - throw new IllegalStateException("The transport factory is closed."); - } - return new BinderTransport.BinderClientTransport( - sourceContext, - channelCredentials, - (AndroidComponentAddress) addr, - targetUserHandle, - bindServiceFlags, - mainThreadExecutor, - scheduledExecutorPool, - offloadExecutorPool, - securityPolicy, - inboundParcelablePolicy, - OneWayBinderProxy.IDENTITY_DECORATOR, - options.getEagAttributes()); - } - - @Override - public ScheduledExecutorService getScheduledExecutorService() { - return executorService; - } - - @Override - public SwapChannelCredentialsResult swapChannelCredentials(ChannelCredentials channelCreds) { - return null; - } - - @Override - public void close() { - closed = true; - executorService = scheduledExecutorPool.returnObject(executorService); - offloadExecutor = offloadExecutorPool.returnObject(offloadExecutor); - } - - @Override - public Collection> getSupportedSocketAddressTypes() { - return Collections.singleton(AndroidComponentAddress.class); - } + @Override + public ManagedChannel build() { + transportFactoryBuilder.setOffloadExecutorPool( + managedChannelImplBuilder.getOffloadExecutorPool()); + return super.build(); } } diff --git a/binder/src/main/java/io/grpc/binder/internal/BinderClientTransportFactory.java b/binder/src/main/java/io/grpc/binder/internal/BinderClientTransportFactory.java new file mode 100644 index 0000000000..abaf07b002 --- /dev/null +++ b/binder/src/main/java/io/grpc/binder/internal/BinderClientTransportFactory.java @@ -0,0 +1,195 @@ +/* + * Copyright 2024 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.internal; + +import static com.google.common.base.Preconditions.checkNotNull; + +import android.content.Context; +import android.os.UserHandle; +import androidx.core.content.ContextCompat; +import io.grpc.ChannelCredentials; +import io.grpc.ChannelLogger; +import io.grpc.Internal; +import io.grpc.binder.AndroidComponentAddress; +import io.grpc.binder.BindServiceFlags; +import io.grpc.binder.BinderChannelCredentials; +import io.grpc.binder.InboundParcelablePolicy; +import io.grpc.binder.SecurityPolicies; +import io.grpc.binder.SecurityPolicy; +import io.grpc.internal.ClientTransportFactory; +import io.grpc.internal.GrpcUtil; +import io.grpc.internal.ManagedChannelImplBuilder.ClientTransportFactoryBuilder; +import io.grpc.internal.ObjectPool; +import io.grpc.internal.SharedResourcePool; +import java.net.SocketAddress; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledExecutorService; +import javax.annotation.Nullable; + +/** + * Creates new binder transports. + */ +@Internal +public final class BinderClientTransportFactory implements ClientTransportFactory { + final Context sourceContext; + final BinderChannelCredentials channelCredentials; + final Executor mainThreadExecutor; + final ObjectPool scheduledExecutorPool; + final ObjectPool offloadExecutorPool; + final SecurityPolicy securityPolicy; + @Nullable + final UserHandle targetUserHandle; + final BindServiceFlags bindServiceFlags; + final InboundParcelablePolicy inboundParcelablePolicy; + final OneWayBinderProxy.Decorator binderDecorator; + + ScheduledExecutorService executorService; + Executor offloadExecutor; + private boolean closed; + + private BinderClientTransportFactory(Builder builder) { + sourceContext = checkNotNull(builder.sourceContext); + channelCredentials = checkNotNull(builder.channelCredentials); + mainThreadExecutor = builder.mainThreadExecutor != null ? + builder.mainThreadExecutor : ContextCompat.getMainExecutor(sourceContext); + scheduledExecutorPool = checkNotNull(builder.scheduledExecutorPool); + offloadExecutorPool = checkNotNull(builder.offloadExecutorPool); + securityPolicy = checkNotNull(builder.securityPolicy); + targetUserHandle = builder.targetUserHandle; + bindServiceFlags = checkNotNull(builder.bindServiceFlags); + inboundParcelablePolicy = checkNotNull(builder.inboundParcelablePolicy); + binderDecorator = checkNotNull(builder.binderDecorator); + + executorService = scheduledExecutorPool.getObject(); + offloadExecutor = offloadExecutorPool.getObject(); + } + + @Override + public BinderTransport.BinderClientTransport newClientTransport( + SocketAddress addr, ClientTransportOptions options, ChannelLogger channelLogger) { + if (closed) { + throw new IllegalStateException("The transport factory is closed."); + } + return new BinderTransport.BinderClientTransport(this, (AndroidComponentAddress) addr, options); + } + + @Override + public ScheduledExecutorService getScheduledExecutorService() { + return executorService; + } + + @Override + public SwapChannelCredentialsResult swapChannelCredentials(ChannelCredentials channelCreds) { + return null; + } + + @Override + public void close() { + closed = true; + executorService = scheduledExecutorPool.returnObject(executorService); + offloadExecutor = offloadExecutorPool.returnObject(offloadExecutor); + } + + @Override + public Collection> getSupportedSocketAddressTypes() { + return Collections.singleton(AndroidComponentAddress.class); + } + + /** + * Allows fluent construction of ClientTransportFactory. + */ + public static final class Builder implements ClientTransportFactoryBuilder { + // Required. + Context sourceContext; + ObjectPool offloadExecutorPool; + + // Optional. + BinderChannelCredentials channelCredentials = BinderChannelCredentials.forDefault(); + Executor mainThreadExecutor; // Default filled-in at build time once sourceContext is decided. + ObjectPool scheduledExecutorPool = + SharedResourcePool.forResource(GrpcUtil.TIMER_SERVICE); + SecurityPolicy securityPolicy = SecurityPolicies.internalOnly(); + @Nullable + UserHandle targetUserHandle; + BindServiceFlags bindServiceFlags = BindServiceFlags.DEFAULTS; + InboundParcelablePolicy inboundParcelablePolicy = InboundParcelablePolicy.DEFAULT; + OneWayBinderProxy.Decorator binderDecorator = OneWayBinderProxy.IDENTITY_DECORATOR; + + @Override + public BinderClientTransportFactory buildClientTransportFactory() { + return new BinderClientTransportFactory(this); + } + + public Builder setSourceContext(Context sourceContext) { + this.sourceContext = checkNotNull(sourceContext); + return this; + } + + public Builder setOffloadExecutorPool( + ObjectPool offloadExecutorPool) { + this.offloadExecutorPool = checkNotNull(offloadExecutorPool, "offloadExecutorPool"); + return this; + } + + public Builder setChannelCredentials(BinderChannelCredentials channelCredentials) { + this.channelCredentials = checkNotNull(channelCredentials, "channelCredentials"); + return this; + } + + public Builder setMainThreadExecutor(Executor mainThreadExecutor) { + this.mainThreadExecutor = checkNotNull(mainThreadExecutor, "mainThreadExecutor"); + return this; + } + + public Builder setScheduledExecutorPool( + ObjectPool scheduledExecutorPool) { + this.scheduledExecutorPool = checkNotNull(scheduledExecutorPool, "scheduledExecutorPool"); + return this; + } + + public Builder setSecurityPolicy(SecurityPolicy securityPolicy) { + this.securityPolicy = checkNotNull(securityPolicy, "securityPolicy"); + return this; + } + + public Builder setTargetUserHandle(@Nullable UserHandle targetUserHandle) { + this.targetUserHandle = targetUserHandle; + return this; + } + + public Builder setBindServiceFlags(BindServiceFlags bindServiceFlags) { + this.bindServiceFlags = checkNotNull(bindServiceFlags, "bindServiceFlags"); + return this; + } + + public Builder setInboundParcelablePolicy(InboundParcelablePolicy inboundParcelablePolicy) { + this.inboundParcelablePolicy = checkNotNull(inboundParcelablePolicy, "inboundParcelablePolicy"); + return this; + } + + /** + * Decorates both the "endpoint" and "server" binders, for fault injection. + * + *

Optional. If absent, these objects will go undecorated. + */ + public Builder setBinderDecorator(OneWayBinderProxy.Decorator binderDecorator) { + this.binderDecorator = checkNotNull(binderDecorator, "binderDecorator"); + return this; + } + } +} 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 4a33adb215..2703e0ae95 100644 --- a/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java +++ b/binder/src/main/java/io/grpc/binder/internal/BinderTransport.java @@ -28,7 +28,6 @@ import android.os.Parcel; 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.base.Verify; @@ -47,11 +46,10 @@ import io.grpc.ServerStreamTracer; import io.grpc.Status; 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; +import io.grpc.internal.ClientTransportFactory.ClientTransportOptions; import io.grpc.internal.ConnectionClientTransport; import io.grpc.internal.FailingClientStream; import io.grpc.internal.GrpcAttributes; @@ -573,41 +571,34 @@ public abstract class BinderTransport /** * Constructs a new transport instance. * - * @param binderDecorator used to decorate both the "endpoint" and "server" binders, for fault - * injection. + * @param factory parameters common to all a Channel's transports + * @param targetAddress the fully resolved and load-balanced server address + * @param options other parameters that can vary as transports come and go within a Channel */ public BinderClientTransport( - Context sourceContext, - BinderChannelCredentials channelCredentials, + BinderClientTransportFactory factory, AndroidComponentAddress targetAddress, - @Nullable UserHandle targetUserHandle, - BindServiceFlags bindServiceFlags, - Executor mainThreadExecutor, - ObjectPool executorServicePool, - ObjectPool offloadExecutorPool, - SecurityPolicy securityPolicy, - InboundParcelablePolicy inboundParcelablePolicy, - OneWayBinderProxy.Decorator binderDecorator, - Attributes eagAttrs) { + ClientTransportOptions options) { super( - executorServicePool, - buildClientAttributes(eagAttrs, sourceContext, targetAddress, inboundParcelablePolicy), - binderDecorator, - buildLogId(sourceContext, targetAddress)); - this.offloadExecutorPool = offloadExecutorPool; - this.securityPolicy = securityPolicy; + factory.scheduledExecutorPool, + buildClientAttributes(options.getEagAttributes(), + factory.sourceContext, targetAddress, factory.inboundParcelablePolicy), + factory.binderDecorator, + buildLogId(factory.sourceContext, targetAddress)); + this.offloadExecutorPool = factory.offloadExecutorPool; + this.securityPolicy = factory.securityPolicy; this.offloadExecutor = offloadExecutorPool.getObject(); numInUseStreams = new AtomicInteger(); pingTracker = new PingTracker(Ticker.systemTicker(), (id) -> sendPing(id)); serviceBinding = new ServiceBinding( - mainThreadExecutor, - sourceContext, - channelCredentials, + factory.mainThreadExecutor, + factory.sourceContext, + factory.channelCredentials, targetAddress.asBindIntent(), - targetUserHandle, - bindServiceFlags.toInteger(), + factory.targetUserHandle, + factory.bindServiceFlags.toInteger(), this); }