2222import android .os .DeadObjectException ;
2323import android .os .Parcel ;
2424import android .os .RemoteException ;
25- import androidx .core .content .ContextCompat ;
2625import androidx .test .core .app .ApplicationProvider ;
2726import androidx .test .ext .junit .runners .AndroidJUnit4 ;
27+ import com .google .common .util .concurrent .SettableFuture ;
2828import com .google .protobuf .Empty ;
29- import io .grpc .Attributes ;
3029import io .grpc .CallOptions ;
3130import io .grpc .ClientStreamTracer ;
3231import io .grpc .Metadata ;
3635import io .grpc .Status ;
3736import io .grpc .Status .Code ;
3837import io .grpc .binder .AndroidComponentAddress ;
39- import io .grpc .binder .BindServiceFlags ;
40- import io .grpc .binder .BinderChannelCredentials ;
4138import io .grpc .binder .BinderServerBuilder ;
4239import io .grpc .binder .HostServices ;
43- import io .grpc .binder .InboundParcelablePolicy ;
44- import io .grpc .binder .SecurityPolicies ;
4540import io .grpc .binder .SecurityPolicy ;
41+ import io .grpc .binder .internal .OneWayBinderProxies .BlackHoleOneWayBinderProxy ;
4642import io .grpc .binder .internal .OneWayBinderProxies .BlockingBinderDecorator ;
4743import io .grpc .binder .internal .OneWayBinderProxies .ThrowingOneWayBinderProxy ;
4844import io .grpc .internal .ClientStream ;
5955import java .util .ArrayDeque ;
6056import java .util .Deque ;
6157import java .util .concurrent .BlockingQueue ;
58+ import java .util .concurrent .ExecutorService ;
6259import java .util .concurrent .Executors ;
6360import java .util .concurrent .LinkedBlockingQueue ;
6461import java .util .concurrent .ScheduledExecutorService ;
62+ import java .util .concurrent .TimeUnit ;
6563import javax .annotation .Nullable ;
6664import javax .annotation .concurrent .GuardedBy ;
6765import org .junit .After ;
7775 */
7876@ RunWith (AndroidJUnit4 .class )
7977public final class BinderClientTransportTest {
78+ private static final long TIMEOUT_SECONDS = 5 ;
79+
8080 private static final ClientStreamTracer [] tracers = new ClientStreamTracer [] {
8181 new ClientStreamTracer () {}
8282 };
@@ -100,9 +100,12 @@ public final class BinderClientTransportTest {
100100
101101 AndroidComponentAddress serverAddress ;
102102 BinderTransport .BinderClientTransport transport ;
103+ BlockingSecurityPolicy blockingSecurityPolicy = new BlockingSecurityPolicy ();
103104
104105 private final ObjectPool <ScheduledExecutorService > executorServicePool =
105106 new FixedObjectPool <>(Executors .newScheduledThreadPool (1 ));
107+ private final ObjectPool <ScheduledExecutorService > offloadServicePool =
108+ new FixedObjectPool <>(Executors .newScheduledThreadPool (1 ));
106109 private final TestTransportListener transportListener = new TestTransportListener ();
107110 private final TestStreamListener streamListener = new TestStreamListener ();
108111
@@ -146,7 +149,7 @@ private class BinderClientTransportBuilder {
146149 final BinderClientTransportFactory .Builder factoryBuilder = new BinderClientTransportFactory .Builder ()
147150 .setSourceContext (appContext )
148151 .setScheduledExecutorPool (executorServicePool )
149- .setOffloadExecutorPool (executorServicePool );
152+ .setOffloadExecutorPool (offloadServicePool );
150153
151154 public BinderClientTransportBuilder setSecurityPolicy (SecurityPolicy securityPolicy ) {
152155 factoryBuilder .setSecurityPolicy (securityPolicy );
@@ -159,6 +162,11 @@ public BinderClientTransportBuilder setBinderDecorator(
159162 return this ;
160163 }
161164
165+ public BinderClientTransportBuilder setReadyTimeoutMillis (int timeoutMillis ) {
166+ factoryBuilder .setReadyTimeoutMillis (timeoutMillis );
167+ return this ;
168+ }
169+
162170 public BinderTransport .BinderClientTransport build () {
163171 return factoryBuilder .buildClientTransportFactory ()
164172 .newClientTransport (serverAddress , new ClientTransportOptions (), null );
@@ -167,9 +175,19 @@ public BinderTransport.BinderClientTransport build() {
167175
168176 @ After
169177 public void tearDown () throws Exception {
178+ blockingSecurityPolicy .provideNextCheckAuthorizationResult (Status .ABORTED );
170179 transport .shutdownNow (Status .OK );
171180 HostServices .awaitServiceShutdown ();
172- executorServicePool .getObject ().shutdownNow ();
181+ shutdownAndTerminate (executorServicePool .getObject ());
182+ shutdownAndTerminate (offloadServicePool .getObject ());
183+ }
184+
185+ private static void shutdownAndTerminate (ExecutorService executorService )
186+ throws InterruptedException {
187+ executorService .shutdownNow ();
188+ if (!executorService .awaitTermination (TIMEOUT_SECONDS , TimeUnit .SECONDS )) {
189+ throw new AssertionError ("executor failed to terminate promptly" );
190+ }
173191 }
174192
175193 @ Test
@@ -261,23 +279,22 @@ public void testMessageProducerClosedAfterStream_b169313545() throws Exception {
261279 }
262280
263281 @ Test
264- public void testNewStreamBeforeTransportReadyFails () throws InterruptedException {
282+ public void testNewStreamBeforeTransportReadyFails () throws Exception {
265283 // Use a special SecurityPolicy that lets us act before the transport is setup/ready.
266- BlockingSecurityPolicy bsp = new BlockingSecurityPolicy ();
267- transport = new BinderClientTransportBuilder ().setSecurityPolicy (bsp ).build ();
284+ transport = new BinderClientTransportBuilder ().setSecurityPolicy (blockingSecurityPolicy ).build ();
268285 transport .start (transportListener ).run ();
269286 ClientStream stream =
270287 transport .newStream (streamingMethodDesc , new Metadata (), CallOptions .DEFAULT , tracers );
271288 stream .start (streamListener );
272289 assertThat (streamListener .awaitClose ().getCode ()).isEqualTo (Code .INTERNAL );
273290
274291 // Unblock the SETUP_TRANSPORT handshake and make sure it becomes ready in the usual way.
275- bsp .provideNextCheckAuthorizationResult (Status .OK );
292+ blockingSecurityPolicy .provideNextCheckAuthorizationResult (Status .OK );
276293 transportListener .awaitReady ();
277294 }
278295
279296 @ Test
280- public void testTxnFailureDuringSetup () throws InterruptedException {
297+ public void testTxnFailureDuringSetup () throws Exception {
281298 BlockingBinderDecorator <ThrowingOneWayBinderProxy > decorator = new BlockingBinderDecorator <>();
282299 transport = new BinderClientTransportBuilder ()
283300 .setBinderDecorator (decorator )
@@ -304,7 +321,7 @@ public void testTxnFailureDuringSetup() throws InterruptedException {
304321 }
305322
306323 @ Test
307- public void testTxnFailurePostSetup () throws InterruptedException {
324+ public void testTxnFailurePostSetup () throws Exception {
308325 BlockingBinderDecorator <ThrowingOneWayBinderProxy > decorator = new BlockingBinderDecorator <>();
309326 transport = new BinderClientTransportBuilder ()
310327 .setBinderDecorator (decorator )
@@ -332,59 +349,82 @@ public void testTxnFailurePostSetup() throws InterruptedException {
332349 assertThat (streamStatus .getCause ()).isSameInstanceAs (doe );
333350 }
334351
352+ @ Test
353+ public void testBlackHoleEndpointConnectTimeout () throws Exception {
354+ BlockingBinderDecorator <BlackHoleOneWayBinderProxy > decorator = new BlockingBinderDecorator <>();
355+ transport = new BinderClientTransportBuilder ()
356+ .setBinderDecorator (decorator )
357+ .setReadyTimeoutMillis (1_234 )
358+ .build ();
359+ transport .start (transportListener ).run ();
360+ BlackHoleOneWayBinderProxy endpointBinder = new BlackHoleOneWayBinderProxy (
361+ decorator .takeNextRequest ());
362+ endpointBinder .dropAllTransactions (true );
363+ decorator .putNextResult (endpointBinder );
364+ Status transportStatus = transportListener .awaitShutdown ();
365+ assertThat (transportStatus .getCode ()).isEqualTo (Code .DEADLINE_EXCEEDED );
366+ assertThat (transportStatus .getDescription ()).contains ("1234" );
367+ transportListener .awaitTermination ();
368+ }
369+
370+ @ Test
371+ public void testBlackHoleSecurityPolicyConnectTimeout () throws Exception {
372+ transport = new BinderClientTransportBuilder ()
373+ .setSecurityPolicy (blockingSecurityPolicy )
374+ .setReadyTimeoutMillis (1_234 )
375+ .build ();
376+ transport .start (transportListener ).run ();
377+ Status transportStatus = transportListener .awaitShutdown ();
378+ assertThat (transportStatus .getCode ()).isEqualTo (Code .DEADLINE_EXCEEDED );
379+ assertThat (transportStatus .getDescription ()).contains ("1234" );
380+ transportListener .awaitTermination ();
381+ blockingSecurityPolicy .provideNextCheckAuthorizationResult (Status .OK );
382+ }
383+
335384 private static void startAndAwaitReady (
336- BinderTransport .BinderClientTransport transport , TestTransportListener transportListener ) {
385+ BinderTransport .BinderClientTransport transport , TestTransportListener transportListener )
386+ throws Exception {
337387 transport .start (transportListener ).run ();
338388 transportListener .awaitReady ();
339389 }
340390
341391 private static final class TestTransportListener implements ManagedClientTransport .Listener {
342- @ GuardedBy ("this" )
343- private boolean ready ;
344-
345392 public boolean inUse ;
346- @ Nullable public Status shutdownStatus ;
347- public boolean terminated ;
393+ private final SettableFuture <Boolean > isReady = SettableFuture .create ();
394+ private final SettableFuture <Status > shutdownStatus = SettableFuture .create ();
395+ private final SettableFuture <Boolean > isTerminated = SettableFuture .create ();
348396
349397 @ Override
350- public synchronized void transportShutdown (Status shutdownStatus ) {
351- this .shutdownStatus = shutdownStatus ;
352- notifyAll ();
398+ public void transportShutdown (Status shutdownStatus ) {
399+ if (!this .shutdownStatus .set (shutdownStatus )) {
400+ throw new IllegalStateException ("transportShutdown() already called" );
401+ }
353402 }
354403
355- public synchronized Status awaitShutdown () throws InterruptedException {
356- while (shutdownStatus == null ) {
357- wait ();
358- }
359- return shutdownStatus ;
404+ public Status awaitShutdown () throws Exception {
405+ return shutdownStatus .get (TIMEOUT_SECONDS , TimeUnit .SECONDS );
360406 }
361407
362408 @ Override
363- public synchronized void transportTerminated () {
364- terminated = true ;
365- notifyAll ();
409+ public void transportTerminated () {
410+ if (!isTerminated .set (true )) {
411+ throw new IllegalStateException ("isTerminated() already called" );
412+ }
366413 }
367414
368- public synchronized void awaitTermination () throws InterruptedException {
369- while (!terminated ) {
370- wait ();
371- }
415+ public void awaitTermination () throws Exception {
416+ isTerminated .get (TIMEOUT_SECONDS , TimeUnit .SECONDS );
372417 }
373418
374419 @ Override
375- public synchronized void transportReady () {
376- ready = true ;
377- notifyAll ();
420+ public void transportReady () {
421+ if (!isReady .set (true )) {
422+ throw new IllegalStateException ("isTerminated() already called" );
423+ }
378424 }
379425
380- public synchronized void awaitReady () {
381- while (!ready ) {
382- try {
383- wait ();
384- } catch (InterruptedException inte ) {
385- throw new AssertionError ("Interrupted waiting for ready" );
386- }
387- }
426+ public void awaitReady () throws Exception {
427+ isReady .get (TIMEOUT_SECONDS , TimeUnit .SECONDS );
388428 }
389429
390430 @ Override
0 commit comments