Skip to content
Open
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c11f45d
Pre-factor out the guts of the BinderClientTransport handshake
jdcormie Aug 20, 2025
8486ac1
don't rename binder
jdcormie Aug 20, 2025
efd855f
ComponentName
jdcormie Aug 20, 2025
68535c1
Merge branch 'master' into isolated-process-2
jdcormie Aug 21, 2025
80eee58
Pre-factor out the guts of the BinderClientTransport handshake
jdcormie Aug 20, 2025
6fd88de
Merge branch 'isolated-process-2' of https://github.com/jdcormie/grpc…
jdcormie Aug 28, 2025
643144c
Pre-factor out the guts of the BinderClientTransport handshake
jdcormie Aug 20, 2025
aedc3bc
ComponentName
jdcormie Aug 20, 2025
6c73cca
Merge remote-tracking branch 'origin/master' into isolated-process-2
jdcormie Aug 28, 2025
ddf4f2f
Merge branch 'master' of https://github.com/grpc/grpc-java into isola…
jdcormie Aug 29, 2025
c208ab3
factor out the decorate() and wrap() calls common to all handshakes
jdcormie Aug 29, 2025
a5a6057
Pre-factor out the guts of the BinderClientTransport handshake
jdcormie Aug 20, 2025
0400204
Pre-factor out the guts of the BinderClientTransport handshake
jdcormie Aug 20, 2025
1bb363c
don't rename binder
jdcormie Aug 20, 2025
d77c6f5
ComponentName
jdcormie Aug 20, 2025
1cfb40b
Pre-factor out the guts of the BinderClientTransport handshake
jdcormie Aug 20, 2025
beb8f6e
ComponentName
jdcormie Aug 20, 2025
3baa4b9
factor out the decorate() and wrap() calls common to all handshakes
jdcormie Aug 29, 2025
24e3ba1
Pre-factor out the guts of the BinderClientTransport handshake
jdcormie Aug 20, 2025
9ff81d9
Pre-factor out the guts of the BinderClientTransport handshake
jdcormie Aug 20, 2025
72024df
ComponentName
jdcormie Aug 20, 2025
0e2e9bc
Pre-factor out the guts of the BinderClientTransport handshake
jdcormie Aug 20, 2025
b60b721
ComponentName
jdcormie Aug 20, 2025
883e471
factor out the decorate() and wrap() calls common to all handshakes
jdcormie Aug 29, 2025
9e86b35
Merge branch 'isolated-process-2' of https://github.com/jdcormie/grpc…
jdcormie Sep 13, 2025
1896db3
Merge remote-tracking branch 'refs/remotes/origin/isolated-process-2'…
jdcormie Sep 16, 2025
25df7e8
fix imports
jdcormie Sep 16, 2025
9711907
Merge branch 'grpc:master' into isolated-process-2
jdcormie Sep 16, 2025
2086335
interface -> abstract class
jdcormie Sep 16, 2025
d53fe91
sync comments
jdcormie Sep 16, 2025
01f46c7
address review comments
jdcormie Oct 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.Process;
import androidx.annotation.BinderThread;
import androidx.annotation.MainThread;
import com.google.common.base.Ticker;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
Expand Down Expand Up @@ -72,6 +74,9 @@ public final class BinderClientTransport extends BinderTransport
private final SecurityPolicy securityPolicy;
private final Bindable serviceBinding;

@GuardedBy("this")
private final ClientHandshake handshake;

/** Number of ongoing calls which keep this transport "in-use". */
private final AtomicInteger numInUseStreams;

Expand Down Expand Up @@ -122,6 +127,7 @@ public BinderClientTransport(
Boolean preAuthServerOverride = options.getEagAttributes().get(PRE_AUTH_SERVER_OVERRIDE);
this.preAuthorizeServer =
preAuthServerOverride != null ? preAuthServerOverride : factory.preAuthorizeServers;
this.handshake = new LegacyClientHandshake();
numInUseStreams = new AtomicInteger();
pingTracker = new PingTracker(Ticker.systemTicker(), (id) -> sendPing(id));

Expand All @@ -146,7 +152,9 @@ void releaseExecutors() {

@Override
public synchronized void onBound(IBinder binder) {
sendSetupTransaction(binderDecorator.decorate(OneWayBinderProxy.wrap(binder, offloadExecutor)));
OneWayBinderProxy binderProxy = OneWayBinderProxy.wrap(binder, offloadExecutor);
binderProxy = binderDecorator.decorate(binderProxy);
handshake.onBound(binderProxy);
}

@Override
Expand Down Expand Up @@ -329,7 +337,6 @@ void notifyTerminated() {
@Override
@GuardedBy("this")
protected void handleSetupTransport(Parcel parcel) {
int remoteUid = Binder.getCallingUid();
if (inState(TransportState.SETUP)) {
int version = parcel.readInt();
IBinder binder = parcel.readStrongBinder();
Expand All @@ -339,23 +346,8 @@ protected void handleSetupTransport(Parcel parcel) {
shutdownInternal(
Status.UNAVAILABLE.withDescription("Malformed SETUP_TRANSPORT data"), true);
} else {
restrictIncomingBinderToCallsFrom(remoteUid);
attributes = setSecurityAttrs(attributes, remoteUid);
authResultFuture = checkServerAuthorizationAsync(remoteUid);
Futures.addCallback(
authResultFuture,
new FutureCallback<Status>() {
@Override
public void onSuccess(Status result) {
handleAuthResult(binder, result);
}

@Override
public void onFailure(Throwable t) {
handleAuthResult(t);
}
},
offloadExecutor);
OneWayBinderProxy binderProxy = OneWayBinderProxy.wrap(binder, offloadExecutor);
handshake.handleSetupTransport(binderProxy);
}
}
}
Expand All @@ -366,29 +358,70 @@ private ListenableFuture<Status> checkServerAuthorizationAsync(int remoteUid) {
: Futures.submit(() -> securityPolicy.checkAuthorization(remoteUid), offloadExecutor);
}

private synchronized void handleAuthResult(IBinder binder, Status authorization) {
if (inState(TransportState.SETUP)) {
if (!authorization.isOk()) {
shutdownInternal(authorization, true);
} else if (!setOutgoingBinder(OneWayBinderProxy.wrap(binder, offloadExecutor))) {
shutdownInternal(
Status.UNAVAILABLE.withDescription("Failed to observe outgoing binder"), true);
} else {
// Check state again, since a failure inside setOutgoingBinder (or a callback it
// triggers), could have shut us down.
if (!isShutdown()) {
setState(TransportState.READY);
attributes = clientTransportListener.filterTransport(attributes);
clientTransportListener.transportReady();
if (readyTimeoutFuture != null) {
readyTimeoutFuture.cancel(false);
readyTimeoutFuture = null;
private final class LegacyClientHandshake implements ClientHandshake {
@Override
@MainThread
@GuardedBy("BinderClientTransport.this") // By way of @GuardedBy("this") `handshake` member.
public void onBound(OneWayBinderProxy binder) {
sendSetupTransaction(binder);
}

@Override
@BinderThread
@GuardedBy("BinderClientTransport.this") // By way of @GuardedBy("this") `handshake` member.
public void handleSetupTransport(OneWayBinderProxy binder) {
int remoteUid = Binder.getCallingUid();
restrictIncomingBinderToCallsFrom(remoteUid);
attributes = setSecurityAttrs(attributes, remoteUid);
authResultFuture = checkServerAuthorizationAsync(remoteUid);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about the plans for the next PR, but it seems like authResultFuture is never used by the parent class anymore except during cancellation.

It feels a bit weird to see one class setting another one's fields, even if they are nested. Could we move authResultFuture into this one and add a cancel method to ClientHandshake?

Ditto possibly for attributes; couldn't fully track all the places where it is used.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other ClientHandshake impl will also need to set and potentially cancel authResultFuture so I'd like to keep this member in some shared location. Fortunately I expect this issue will be going away soon with my proposed fix for #12283 (jdcormie@d0bcf4b). LMK if that would be satisfactory.

As for attributes, deciding how and when to update the transport attributes with the peer's UID is one of the core responsibilities of ClientHandshake. As I mentioned in the other comment, the relationship between BinderClientTransport and the ClientHandshake impls is intentionally intimate. My goal with ClientHandshake isn't actually to create an arm's length relationship with the transport, rather it's just to avoid writing if (flag) { /* v1 */ } else { /* v2 */} all over the place. LMK if you feel strongly and/or suggest a different approach.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re: "plans for the next PR" I just uploaded it as #12398. However, I recommend reviewing it as CR/814340095 which will let you ignore the diffbase (CR/808247923).

The critique view of this PR (CR/808247923) also does a much better job of showing how this PR is really just moving code around without changing it.

Futures.addCallback(
authResultFuture,
new FutureCallback<Status>() {
@Override
public void onSuccess(Status result) {
synchronized (BinderClientTransport.this) {
handleAuthResult(binder, result);
}
}

@Override
public void onFailure(Throwable t) {
BinderClientTransport.this.handleAuthResult(t);
}
},
offloadExecutor);
}

@GuardedBy("BinderClientTransport.this")
private void handleAuthResult(OneWayBinderProxy binder, Status authorization) {
if (inState(TransportState.SETUP)) {
if (!authorization.isOk()) {
shutdownInternal(authorization, true);
} else if (!setOutgoingBinder(binder)) {
shutdownInternal(
Status.UNAVAILABLE.withDescription("Failed to observe outgoing binder"), true);
} else {
// Check state again, since a failure inside setOutgoingBinder (or a callback it
// triggers), could have shut us down.
if (!isShutdown()) {
onHandshakeComplete();
}
}
}
}
}

@GuardedBy("this")
private void onHandshakeComplete() {
setState(TransportState.READY);
attributes = clientTransportListener.filterTransport(attributes);
clientTransportListener.transportReady();
if (readyTimeoutFuture != null) {
readyTimeoutFuture.cancel(false);
readyTimeoutFuture = null;
}
}

private synchronized void handleAuthResult(Throwable t) {
shutdownInternal(
Status.INTERNAL.withDescription("Could not evaluate SecurityPolicy").withCause(t), true);
Expand All @@ -400,6 +433,27 @@ protected void handlePingResponse(Parcel parcel) {
pingTracker.onPingResponse(parcel.readInt());
}

/**
* A base for all implementations of the client handshake.
*
* <p>Supports a clean migration away from the legacy approach, one client at a time.
*/
private interface ClientHandshake {
/**
* Notifies the implementation that the binding has succeeded and we are now connected to the
* server's "endpoint" which can be reached at 'endpointBinder'.
*/
@MainThread
void onBound(OneWayBinderProxy endpointBinder);

/**
* Notifies the implementation that we've received a valid SETUP_TRANSPORT transaction from a
* server that can be reached at 'serverBinder'.
*/
@BinderThread
void handleSetupTransport(OneWayBinderProxy serverBinder);
}

private static ClientStream newFailingClientStream(
Status failure, Attributes attributes, Metadata headers, ClientStreamTracer[] tracers) {
StatsTraceContext statsTraceContext =
Expand Down