Use the builder pattern to replace BinderClientTransport's long ctor arg list (#11220)

This commit is contained in:
John Cormie 2024-05-23 11:50:37 -07:00 committed by GitHub
parent 6aa063990a
commit 0b5f38d942
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 254 additions and 189 deletions

View File

@ -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);
}
}

View File

@ -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

View File

@ -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<ScheduledExecutorService> 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<ScheduledExecutorService> scheduledExecutorPool;
private final ObjectPool<? extends Executor> 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<ScheduledExecutorService> scheduledExecutorPool,
ObjectPool<? extends Executor> 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<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
return Collections.singleton(AndroidComponentAddress.class);
}
@Override
public ManagedChannel build() {
transportFactoryBuilder.setOffloadExecutorPool(
managedChannelImplBuilder.getOffloadExecutorPool());
return super.build();
}
}

View File

@ -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<ScheduledExecutorService> scheduledExecutorPool;
final ObjectPool<? extends Executor> 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<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
return Collections.singleton(AndroidComponentAddress.class);
}
/**
* Allows fluent construction of ClientTransportFactory.
*/
public static final class Builder implements ClientTransportFactoryBuilder {
// Required.
Context sourceContext;
ObjectPool<? extends Executor> offloadExecutorPool;
// Optional.
BinderChannelCredentials channelCredentials = BinderChannelCredentials.forDefault();
Executor mainThreadExecutor; // Default filled-in at build time once sourceContext is decided.
ObjectPool<ScheduledExecutorService> 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<? extends Executor> 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<ScheduledExecutorService> 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.
*
* <p>Optional. If absent, these objects will go undecorated.
*/
public Builder setBinderDecorator(OneWayBinderProxy.Decorator binderDecorator) {
this.binderDecorator = checkNotNull(binderDecorator, "binderDecorator");
return this;
}
}
}

View File

@ -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<ScheduledExecutorService> executorServicePool,
ObjectPool<? extends Executor> 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);
}