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 ;
6463import java .util .concurrent .Executors ;
6564import java .util .concurrent .LinkedBlockingQueue ;
6665import java .util .concurrent .ScheduledExecutorService ;
67- import java .util .concurrent .TimeUnit ;
6866import javax .annotation .Nullable ;
6967import org .junit .After ;
7068import org .junit .Before ;
@@ -193,7 +191,7 @@ public void tearDown() throws Exception {
193191 private static void shutdownAndTerminate (ExecutorService executorService )
194192 throws InterruptedException {
195193 executorService .shutdownNow ();
196- if (!executorService .awaitTermination (TIMEOUT_SECONDS , TimeUnit . SECONDS )) {
194+ if (!executorService .awaitTermination (TIMEOUT_SECONDS , SECONDS )) {
197195 throw new AssertionError ("executor failed to terminate promptly" );
198196 }
199197 }
@@ -375,26 +373,32 @@ public void testBlackHoleEndpointConnectTimeout() throws Exception {
375373
376374 @ Test
377375 public void testBlackHoleSecurityPolicyConnectTimeout () throws Exception {
376+ SettableAsyncSecurityPolicy securityPolicy = new SettableAsyncSecurityPolicy ();
378377 transport =
379378 new BinderClientTransportBuilder ()
380- .setSecurityPolicy (blockingSecurityPolicy )
379+ .setSecurityPolicy (securityPolicy )
381380 .setReadyTimeoutMillis (1_234 )
382381 .build ();
383382 transport .start (transportListener ).run ();
383+ // Take the next authRequest but don't respond to it, in order to trigger the ready timeout.
384+ AuthRequest authRequest = securityPolicy .takeNextAuthRequest (TIMEOUT_SECONDS , SECONDS );
385+
384386 Status transportStatus = transportListener .awaitShutdown ();
385387 assertThat (transportStatus .getCode ()).isEqualTo (Code .DEADLINE_EXCEEDED );
386388 assertThat (transportStatus .getDescription ()).contains ("1234" );
387389 transportListener .awaitTermination ();
388- blockingSecurityPolicy .provideNextCheckAuthorizationResult (Status .OK );
390+
391+ // If the transport gave up waiting on auth, it should cancel its request.
392+ assertThat (authRequest .isCancelled ()).isTrue ();
389393 }
390394
391395 @ Test
392396 public void testAsyncSecurityPolicyFailure () throws Exception {
393397 SettableAsyncSecurityPolicy securityPolicy = new SettableAsyncSecurityPolicy ();
394398 transport = new BinderClientTransportBuilder ().setSecurityPolicy (securityPolicy ).build ();
395399 RuntimeException exception = new NullPointerException ();
396- securityPolicy .setAuthorizationException (exception );
397400 transport .start (transportListener ).run ();
401+ securityPolicy .takeNextAuthRequest (TIMEOUT_SECONDS , SECONDS ).setResult (exception );
398402 Status transportStatus = transportListener .awaitShutdown ();
399403 assertThat (transportStatus .getCode ()).isEqualTo (Code .INTERNAL );
400404 assertThat (transportStatus .getCause ()).isEqualTo (exception );
@@ -405,13 +409,27 @@ public void testAsyncSecurityPolicyFailure() throws Exception {
405409 public void testAsyncSecurityPolicySuccess () throws Exception {
406410 SettableAsyncSecurityPolicy securityPolicy = new SettableAsyncSecurityPolicy ();
407411 transport = new BinderClientTransportBuilder ().setSecurityPolicy (securityPolicy ).build ();
408- securityPolicy .setAuthorizationResult (Status .PERMISSION_DENIED );
409412 transport .start (transportListener ).run ();
413+ securityPolicy
414+ .takeNextAuthRequest (TIMEOUT_SECONDS , SECONDS )
415+ .setResult (Status .PERMISSION_DENIED );
410416 Status transportStatus = transportListener .awaitShutdown ();
411417 assertThat (transportStatus .getCode ()).isEqualTo (Code .PERMISSION_DENIED );
412418 transportListener .awaitTermination ();
413419 }
414420
421+ @ Test
422+ public void testAsyncSecurityPolicyCancelledUponExternalTermination () throws Exception {
423+ SettableAsyncSecurityPolicy securityPolicy = new SettableAsyncSecurityPolicy ();
424+ transport = new BinderClientTransportBuilder ().setSecurityPolicy (securityPolicy ).build ();
425+ transport .start (transportListener ).run ();
426+ AuthRequest authRequest = securityPolicy .takeNextAuthRequest (TIMEOUT_SECONDS , SECONDS );
427+ transport .shutdownNow (Status .UNAVAILABLE ); // 'authRequest' remains unanswered!
428+ transportListener .awaitShutdown ();
429+ transportListener .awaitTermination ();
430+ assertThat (authRequest .isCancelled ()).isTrue ();
431+ }
432+
415433 private static void startAndAwaitReady (
416434 BinderTransport .BinderClientTransport transport , TestTransportListener transportListener )
417435 throws Exception {
@@ -433,7 +451,7 @@ public void transportShutdown(Status shutdownStatus) {
433451 }
434452
435453 public Status awaitShutdown () throws Exception {
436- return shutdownStatus .get (TIMEOUT_SECONDS , TimeUnit . SECONDS );
454+ return shutdownStatus .get (TIMEOUT_SECONDS , SECONDS );
437455 }
438456
439457 @ Override
@@ -444,7 +462,7 @@ public void transportTerminated() {
444462 }
445463
446464 public void awaitTermination () throws Exception {
447- isTerminated .get (TIMEOUT_SECONDS , TimeUnit . SECONDS );
465+ isTerminated .get (TIMEOUT_SECONDS , SECONDS );
448466 }
449467
450468 @ Override
@@ -455,7 +473,7 @@ public void transportReady() {
455473 }
456474
457475 public void awaitReady () throws Exception {
458- isReady .get (TIMEOUT_SECONDS , TimeUnit . SECONDS );
476+ isReady .get (TIMEOUT_SECONDS , SECONDS );
459477 }
460478
461479 @ Override
@@ -571,25 +589,4 @@ public Status checkAuthorization(int uid) {
571589 }
572590 }
573591 }
574-
575- /** An AsyncSecurityPolicy that lets a test specify the outcome of checkAuthorizationAsync(). */
576- static class SettableAsyncSecurityPolicy extends AsyncSecurityPolicy {
577- private SettableFuture <Status > result = SettableFuture .create ();
578-
579- public void clearAuthorizationResult () {
580- result = SettableFuture .create ();
581- }
582-
583- public boolean setAuthorizationResult (Status status ) {
584- return result .set (status );
585- }
586-
587- public boolean setAuthorizationException (Throwable t ) {
588- return result .setException (t );
589- }
590-
591- public ListenableFuture <Status > checkAuthorizationAsync (int uid ) {
592- return Futures .nonCancellationPropagating (result );
593- }
594- }
595592}
0 commit comments