1717package io .grpc .binder .internal ;
1818
1919import static com .google .common .truth .Truth .assertThat ;
20+ import static java .util .concurrent .TimeUnit .SECONDS ;
2021
2122import android .content .Context ;
2223import android .os .DeadObjectException ;
2324import android .os .Parcel ;
2425import android .os .RemoteException ;
2526import androidx .test .core .app .ApplicationProvider ;
2627import androidx .test .ext .junit .runners .AndroidJUnit4 ;
27- import com .google .common .util .concurrent .Futures ;
28- import com .google .common .util .concurrent .ListenableFuture ;
2928import com .google .common .util .concurrent .SettableFuture ;
3029import com .google .errorprone .annotations .CanIgnoreReturnValue ;
3130import com .google .errorprone .annotations .concurrent .GuardedBy ;
3938import io .grpc .Status ;
4039import io .grpc .Status .Code ;
4140import io .grpc .binder .AndroidComponentAddress ;
42- import io .grpc .binder .AsyncSecurityPolicy ;
4341import io .grpc .binder .BinderServerBuilder ;
4442import io .grpc .binder .HostServices ;
4543import io .grpc .binder .SecurityPolicy ;
4644import io .grpc .binder .internal .OneWayBinderProxies .BlackHoleOneWayBinderProxy ;
4745import io .grpc .binder .internal .OneWayBinderProxies .BlockingBinderDecorator ;
4846import io .grpc .binder .internal .OneWayBinderProxies .ThrowingOneWayBinderProxy ;
47+ import io .grpc .binder .internal .SettableAsyncSecurityPolicy .AuthRequest ;
4948import io .grpc .internal .ClientStream ;
5049import io .grpc .internal .ClientStreamListener ;
5150import io .grpc .internal .ClientTransportFactory .ClientTransportOptions ;
6261import java .util .concurrent .ExecutorService ;
6362import java .util .concurrent .Executors ;
6463import java .util .concurrent .ScheduledExecutorService ;
65- import java .util .concurrent .TimeUnit ;
6664import javax .annotation .Nullable ;
6765import org .junit .After ;
6866import org .junit .Before ;
@@ -101,7 +99,7 @@ public final class BinderClientTransportTest {
10199
102100 AndroidComponentAddress serverAddress ;
103101 BinderTransport .BinderClientTransport transport ;
104- SettableAsyncSecurityPolicy blockingSecurityPolicy = new SettableAsyncSecurityPolicy ();
102+ BlockingSecurityPolicy blockingSecurityPolicy = new BlockingSecurityPolicy ();
105103
106104 private final ObjectPool <ScheduledExecutorService > executorServicePool =
107105 new FixedObjectPool <>(Executors .newScheduledThreadPool (1 ));
@@ -172,6 +170,7 @@ public BinderClientTransportBuilder setReadyTimeoutMillis(int timeoutMillis) {
172170 return this ;
173171 }
174172
173+ @ CanIgnoreReturnValue
175174 public BinderClientTransportBuilder setPreAuthorizeServer (boolean preAuthorizeServer ) {
176175 factoryBuilder .setPreAuthorizeServers (preAuthorizeServer );
177176 return this ;
@@ -196,7 +195,7 @@ public void tearDown() throws Exception {
196195 private static void shutdownAndTerminate (ExecutorService executorService )
197196 throws InterruptedException {
198197 executorService .shutdownNow ();
199- if (!executorService .awaitTermination (TIMEOUT_SECONDS , TimeUnit . SECONDS )) {
198+ if (!executorService .awaitTermination (TIMEOUT_SECONDS , SECONDS )) {
200199 throw new AssertionError ("executor failed to terminate promptly" );
201200 }
202201 }
@@ -292,16 +291,16 @@ public void testMessageProducerClosedAfterStream_b169313545() throws Exception {
292291 @ Test
293292 public void testNewStreamBeforeTransportReadyFails () throws Exception {
294293 // Use a special SecurityPolicy that lets us act before the transport is setup/ready.
295- SettableAsyncSecurityPolicy securityPolicy = new SettableAsyncSecurityPolicy ();
296- transport = new BinderClientTransportBuilder ().setSecurityPolicy (securityPolicy ).build ();
294+ transport =
295+ new BinderClientTransportBuilder ().setSecurityPolicy (blockingSecurityPolicy ).build ();
297296 transport .start (transportListener ).run ();
298297 ClientStream stream =
299298 transport .newStream (streamingMethodDesc , new Metadata (), CallOptions .DEFAULT , tracers );
300299 stream .start (streamListener );
301300 assertThat (streamListener .awaitClose ().getCode ()).isEqualTo (Code .INTERNAL );
302301
303302 // Unblock the SETUP_TRANSPORT handshake and make sure it becomes ready in the usual way.
304- securityPolicy . setAuthorizationResult (Status .OK );
303+ blockingSecurityPolicy . provideNextCheckAuthorizationResult (Status .OK );
305304 transportListener .awaitReady ();
306305 }
307306
@@ -380,15 +379,21 @@ public void testBlackHoleEndpointConnectTimeout() throws Exception {
380379 public void testBlackHoleSecurityPolicyAuthTimeout () throws Exception {
381380 transport =
382381 new BinderClientTransportBuilder ()
383- .setSecurityPolicy (blockingSecurityPolicy )
384382 .setPreAuthorizeServer (false )
383+ .setSecurityPolicy (blockingSecurityPolicy )
385384 .setReadyTimeoutMillis (1_234 )
386385 .build ();
387386 transport .start (transportListener ).run ();
387+ // Take the next authRequest but don't respond to it, in order to trigger the ready timeout.
388+ AuthRequest authRequest = securityPolicy .takeNextAuthRequest (TIMEOUT_SECONDS , SECONDS );
389+
388390 Status transportStatus = transportListener .awaitShutdown ();
389391 assertThat (transportStatus .getCode ()).isEqualTo (Code .DEADLINE_EXCEEDED );
390392 assertThat (transportStatus .getDescription ()).contains ("1234" );
391393 transportListener .awaitTermination ();
394+
395+ // If the transport gave up waiting on auth, it should cancel its request.
396+ assertThat (authRequest .isCancelled ()).isTrue ();
392397 }
393398
394399 @ Test
@@ -432,8 +437,8 @@ public void testAsyncSecurityPolicyPreAuthFailure() throws Exception {
432437 .setSecurityPolicy (securityPolicy )
433438 .build ();
434439 RuntimeException exception = new NullPointerException ();
435- securityPolicy .setAuthorizationException (exception );
436440 transport .start (transportListener ).run ();
441+ securityPolicy .takeNextAuthRequest (TIMEOUT_SECONDS , SECONDS ).setResult (exception );
437442 Status transportStatus = transportListener .awaitShutdown ();
438443 assertThat (transportStatus .getCode ()).isEqualTo (Code .INTERNAL );
439444 assertThat (transportStatus .getCause ()).isEqualTo (exception );
@@ -466,12 +471,45 @@ public void testAsyncSecurityPolicyPreAuthSuccess() throws Exception {
466471 .build ();
467472 securityPolicy .setAuthorizationResult (Status .PERMISSION_DENIED .withDescription ("xyzzy" ));
468473 transport .start (transportListener ).run ();
474+ securityPolicy
475+ .takeNextAuthRequest (TIMEOUT_SECONDS , SECONDS )
476+ .setResult (Status .PERMISSION_DENIED );
469477 Status transportStatus = transportListener .awaitShutdown ();
470478 assertThat (transportStatus .getCode ()).isEqualTo (Code .PERMISSION_DENIED );
471479 assertThat (transportStatus .getDescription ()).contains ("xyzzy" );
472480 transportListener .awaitTermination ();
473481 }
474482
483+ @ Test
484+ public void testAsyncSecurityPolicyAuthCancelledUponExternalTermination () throws Exception {
485+ SettableAsyncSecurityPolicy securityPolicy = new SettableAsyncSecurityPolicy ();
486+ transport = new BinderClientTransportBuilder ()
487+ .setSecurityPolicy (securityPolicy )
488+ .setPreAuthorizeServer (false )
489+ .build ();
490+ transport .start (transportListener ).run ();
491+ AuthRequest authRequest = securityPolicy .takeNextAuthRequest (TIMEOUT_SECONDS , SECONDS );
492+ transport .shutdownNow (Status .UNAVAILABLE ); // 'authRequest' remains unanswered!
493+ transportListener .awaitShutdown ();
494+ transportListener .awaitTermination ();
495+ assertThat (authRequest .isCancelled ()).isTrue ();
496+ }
497+
498+ @ Test
499+ public void testAsyncSecurityPolicyPreAuthCancelledUponExternalTermination () throws Exception {
500+ SettableAsyncSecurityPolicy securityPolicy = new SettableAsyncSecurityPolicy ();
501+ transport = new BinderClientTransportBuilder ()
502+ .setSecurityPolicy (securityPolicy )
503+ .setPreAuthorizeServer (true )
504+ .build ();
505+ transport .start (transportListener ).run ();
506+ AuthRequest preAuthRequest = securityPolicy .takeNextAuthRequest (TIMEOUT_SECONDS , SECONDS );
507+ transport .shutdownNow (Status .UNAVAILABLE ); // 'authRequest' remains unanswered!
508+ transportListener .awaitShutdown ();
509+ transportListener .awaitTermination ();
510+ assertThat (preAuthRequest .isCancelled ()).isTrue ();
511+ }
512+
475513 private static void startAndAwaitReady (
476514 BinderTransport .BinderClientTransport transport , TestTransportListener transportListener )
477515 throws Exception {
@@ -493,7 +531,7 @@ public void transportShutdown(Status shutdownStatus) {
493531 }
494532
495533 public Status awaitShutdown () throws Exception {
496- return shutdownStatus .get (TIMEOUT_SECONDS , TimeUnit . SECONDS );
534+ return shutdownStatus .get (TIMEOUT_SECONDS , SECONDS );
497535 }
498536
499537 @ Override
@@ -504,7 +542,7 @@ public void transportTerminated() {
504542 }
505543
506544 public void awaitTermination () throws Exception {
507- isTerminated .get (TIMEOUT_SECONDS , TimeUnit . SECONDS );
545+ isTerminated .get (TIMEOUT_SECONDS , SECONDS );
508546 }
509547
510548 @ Override
@@ -515,7 +553,7 @@ public void transportReady() {
515553 }
516554
517555 public void awaitReady () throws Exception {
518- isReady .get (TIMEOUT_SECONDS , TimeUnit . SECONDS );
556+ isReady .get (TIMEOUT_SECONDS , SECONDS );
519557 }
520558
521559 @ Override
@@ -612,24 +650,23 @@ public synchronized void closed(Status status, RpcProgress rpcProgress, Metadata
612650 }
613651 }
614652
615- /** An AsyncSecurityPolicy that lets a test specify the outcome of checkAuthorizationAsync(). */
616- static class SettableAsyncSecurityPolicy extends AsyncSecurityPolicy {
617- private SettableFuture <Status > result = SettableFuture .create ();
653+ /**
654+ * A SecurityPolicy that blocks the transport authorization check until a test sets the outcome.
655+ */
656+ static class BlockingSecurityPolicy extends SecurityPolicy {
657+ private final BlockingQueue <Status > results = new LinkedBlockingQueue <>();
618658
619- public void clearAuthorizationResult ( ) {
620- result = SettableFuture . create ( );
659+ public void provideNextCheckAuthorizationResult ( Status status ) {
660+ results . add ( status );
621661 }
622662
623- public boolean setAuthorizationResult (Status status ) {
624- return result .set (status );
625- }
626-
627- public boolean setAuthorizationException (Throwable t ) {
628- return result .setException (t );
629- }
630-
631- public ListenableFuture <Status > checkAuthorizationAsync (int uid ) {
632- return Futures .nonCancellationPropagating (result );
663+ @ Override
664+ public Status checkAuthorization (int uid ) {
665+ try {
666+ return results .take ();
667+ } catch (InterruptedException e ) {
668+ return Status .fromThrowable (e );
669+ }
633670 }
634671 }
635672}
0 commit comments