22
22
import android .os .DeadObjectException ;
23
23
import android .os .Parcel ;
24
24
import android .os .RemoteException ;
25
- import androidx .core .content .ContextCompat ;
26
25
import androidx .test .core .app .ApplicationProvider ;
27
26
import androidx .test .ext .junit .runners .AndroidJUnit4 ;
27
+ import com .google .common .util .concurrent .SettableFuture ;
28
28
import com .google .protobuf .Empty ;
29
- import io .grpc .Attributes ;
30
29
import io .grpc .CallOptions ;
31
30
import io .grpc .ClientStreamTracer ;
32
31
import io .grpc .Metadata ;
36
35
import io .grpc .Status ;
37
36
import io .grpc .Status .Code ;
38
37
import io .grpc .binder .AndroidComponentAddress ;
39
- import io .grpc .binder .BindServiceFlags ;
40
- import io .grpc .binder .BinderChannelCredentials ;
41
38
import io .grpc .binder .BinderServerBuilder ;
42
39
import io .grpc .binder .HostServices ;
43
- import io .grpc .binder .InboundParcelablePolicy ;
44
- import io .grpc .binder .SecurityPolicies ;
45
40
import io .grpc .binder .SecurityPolicy ;
41
+ import io .grpc .binder .internal .OneWayBinderProxies .BlackHoleOneWayBinderProxy ;
46
42
import io .grpc .binder .internal .OneWayBinderProxies .BlockingBinderDecorator ;
47
43
import io .grpc .binder .internal .OneWayBinderProxies .ThrowingOneWayBinderProxy ;
48
44
import io .grpc .internal .ClientStream ;
59
55
import java .util .ArrayDeque ;
60
56
import java .util .Deque ;
61
57
import java .util .concurrent .BlockingQueue ;
58
+ import java .util .concurrent .ExecutorService ;
62
59
import java .util .concurrent .Executors ;
63
60
import java .util .concurrent .LinkedBlockingQueue ;
64
61
import java .util .concurrent .ScheduledExecutorService ;
62
+ import java .util .concurrent .TimeUnit ;
65
63
import javax .annotation .Nullable ;
66
64
import javax .annotation .concurrent .GuardedBy ;
67
65
import org .junit .After ;
77
75
*/
78
76
@ RunWith (AndroidJUnit4 .class )
79
77
public final class BinderClientTransportTest {
78
+ private static final long TIMEOUT_SECONDS = 5 ;
79
+
80
80
private static final ClientStreamTracer [] tracers = new ClientStreamTracer [] {
81
81
new ClientStreamTracer () {}
82
82
};
@@ -100,9 +100,12 @@ public final class BinderClientTransportTest {
100
100
101
101
AndroidComponentAddress serverAddress ;
102
102
BinderTransport .BinderClientTransport transport ;
103
+ BlockingSecurityPolicy blockingSecurityPolicy = new BlockingSecurityPolicy ();
103
104
104
105
private final ObjectPool <ScheduledExecutorService > executorServicePool =
105
106
new FixedObjectPool <>(Executors .newScheduledThreadPool (1 ));
107
+ private final ObjectPool <ScheduledExecutorService > offloadServicePool =
108
+ new FixedObjectPool <>(Executors .newScheduledThreadPool (1 ));
106
109
private final TestTransportListener transportListener = new TestTransportListener ();
107
110
private final TestStreamListener streamListener = new TestStreamListener ();
108
111
@@ -146,7 +149,7 @@ private class BinderClientTransportBuilder {
146
149
final BinderClientTransportFactory .Builder factoryBuilder = new BinderClientTransportFactory .Builder ()
147
150
.setSourceContext (appContext )
148
151
.setScheduledExecutorPool (executorServicePool )
149
- .setOffloadExecutorPool (executorServicePool );
152
+ .setOffloadExecutorPool (offloadServicePool );
150
153
151
154
public BinderClientTransportBuilder setSecurityPolicy (SecurityPolicy securityPolicy ) {
152
155
factoryBuilder .setSecurityPolicy (securityPolicy );
@@ -159,6 +162,11 @@ public BinderClientTransportBuilder setBinderDecorator(
159
162
return this ;
160
163
}
161
164
165
+ public BinderClientTransportBuilder setReadyTimeoutMillis (int timeoutMillis ) {
166
+ factoryBuilder .setReadyTimeoutMillis (timeoutMillis );
167
+ return this ;
168
+ }
169
+
162
170
public BinderTransport .BinderClientTransport build () {
163
171
return factoryBuilder .buildClientTransportFactory ()
164
172
.newClientTransport (serverAddress , new ClientTransportOptions (), null );
@@ -167,9 +175,19 @@ public BinderTransport.BinderClientTransport build() {
167
175
168
176
@ After
169
177
public void tearDown () throws Exception {
178
+ blockingSecurityPolicy .provideNextCheckAuthorizationResult (Status .ABORTED );
170
179
transport .shutdownNow (Status .OK );
171
180
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
+ }
173
191
}
174
192
175
193
@ Test
@@ -261,23 +279,22 @@ public void testMessageProducerClosedAfterStream_b169313545() throws Exception {
261
279
}
262
280
263
281
@ Test
264
- public void testNewStreamBeforeTransportReadyFails () throws InterruptedException {
282
+ public void testNewStreamBeforeTransportReadyFails () throws Exception {
265
283
// 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 ();
268
285
transport .start (transportListener ).run ();
269
286
ClientStream stream =
270
287
transport .newStream (streamingMethodDesc , new Metadata (), CallOptions .DEFAULT , tracers );
271
288
stream .start (streamListener );
272
289
assertThat (streamListener .awaitClose ().getCode ()).isEqualTo (Code .INTERNAL );
273
290
274
291
// 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 );
276
293
transportListener .awaitReady ();
277
294
}
278
295
279
296
@ Test
280
- public void testTxnFailureDuringSetup () throws InterruptedException {
297
+ public void testTxnFailureDuringSetup () throws Exception {
281
298
BlockingBinderDecorator <ThrowingOneWayBinderProxy > decorator = new BlockingBinderDecorator <>();
282
299
transport = new BinderClientTransportBuilder ()
283
300
.setBinderDecorator (decorator )
@@ -304,7 +321,7 @@ public void testTxnFailureDuringSetup() throws InterruptedException {
304
321
}
305
322
306
323
@ Test
307
- public void testTxnFailurePostSetup () throws InterruptedException {
324
+ public void testTxnFailurePostSetup () throws Exception {
308
325
BlockingBinderDecorator <ThrowingOneWayBinderProxy > decorator = new BlockingBinderDecorator <>();
309
326
transport = new BinderClientTransportBuilder ()
310
327
.setBinderDecorator (decorator )
@@ -332,59 +349,82 @@ public void testTxnFailurePostSetup() throws InterruptedException {
332
349
assertThat (streamStatus .getCause ()).isSameInstanceAs (doe );
333
350
}
334
351
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
+
335
384
private static void startAndAwaitReady (
336
- BinderTransport .BinderClientTransport transport , TestTransportListener transportListener ) {
385
+ BinderTransport .BinderClientTransport transport , TestTransportListener transportListener )
386
+ throws Exception {
337
387
transport .start (transportListener ).run ();
338
388
transportListener .awaitReady ();
339
389
}
340
390
341
391
private static final class TestTransportListener implements ManagedClientTransport .Listener {
342
- @ GuardedBy ("this" )
343
- private boolean ready ;
344
-
345
392
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 ();
348
396
349
397
@ 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
+ }
353
402
}
354
403
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 );
360
406
}
361
407
362
408
@ 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
+ }
366
413
}
367
414
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 );
372
417
}
373
418
374
419
@ 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
+ }
378
424
}
379
425
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 );
388
428
}
389
429
390
430
@ Override
0 commit comments