22
22
import static org .robolectric .Shadows .shadowOf ;
23
23
24
24
import android .app .Application ;
25
- import android .content .ComponentName ;
26
- import android .content .Intent ;
27
- import android .os .IBinder ;
28
- import android .os .Looper ;
29
- import androidx .lifecycle .LifecycleService ;
30
25
import androidx .test .core .app .ApplicationProvider ;
31
26
import com .google .common .util .concurrent .Futures ;
32
27
import com .google .common .util .concurrent .ListenableFuture ;
42
37
import io .grpc .ServerServiceDefinition ;
43
38
import io .grpc .Status ;
44
39
import io .grpc .StatusRuntimeException ;
45
- import io .grpc .binder .internal .MainThreadScheduledExecutorService ;
46
40
import io .grpc .protobuf .lite .ProtoLiteUtils ;
47
41
import io .grpc .stub .ClientCalls ;
48
42
import io .grpc .stub .ServerCalls ;
49
43
import java .io .IOException ;
50
44
import java .util .concurrent .ArrayBlockingQueue ;
51
- import java .util .concurrent .ScheduledExecutorService ;
52
- import javax .annotation .Nullable ;
53
45
import org .junit .After ;
54
46
import org .junit .Before ;
55
47
import org .junit .Test ;
56
48
import org .junit .runner .RunWith ;
57
- import org .robolectric .Robolectric ;
58
49
import org .robolectric .RobolectricTestRunner ;
59
- import org .robolectric .android .controller .ServiceController ;
50
+ import org .robolectric .annotation .LooperMode ;
51
+ import org .robolectric .annotation .LooperMode .Mode ;
60
52
61
53
@ RunWith (RobolectricTestRunner .class )
54
+ @ LooperMode (Mode .INSTRUMENTATION_TEST )
62
55
public final class RobolectricBinderSecurityTest {
63
56
64
57
private static final String SERVICE_NAME = "fake_service" ;
65
58
private static final String FULL_METHOD_NAME = "fake_service/fake_method" ;
66
59
private final Application context = ApplicationProvider .getApplicationContext ();
67
- private ServiceController < SomeService > controller ;
68
- private SomeService service ;
60
+ private final ArrayBlockingQueue < SettableFuture < Status >> statusesToSet =
61
+ new ArrayBlockingQueue <>( 128 ) ;
69
62
private ManagedChannel channel ;
63
+ private Server server ;
70
64
71
65
@ Before
72
66
public void setUp () {
73
- controller = Robolectric .buildService (SomeService .class );
74
- service = controller .create ().get ();
67
+ AndroidComponentAddress listenAddress =
68
+ AndroidComponentAddress .forRemoteComponent (context .getPackageName (), "HostService" );
69
+
70
+ MethodDescriptor <Empty , Empty > methodDesc = getMethodDescriptor ();
71
+ ServerCallHandler <Empty , Empty > callHandler =
72
+ ServerCalls .asyncUnaryCall (
73
+ (req , respObserver ) -> {
74
+ respObserver .onNext (req );
75
+ respObserver .onCompleted ();
76
+ });
77
+ ServerMethodDefinition <Empty , Empty > methodDef =
78
+ ServerMethodDefinition .create (methodDesc , callHandler );
79
+ ServerServiceDefinition def =
80
+ ServerServiceDefinition .builder (SERVICE_NAME ).addMethod (methodDef ).build ();
81
+
82
+ IBinderReceiver binderReceiver = new IBinderReceiver ();
83
+ server =
84
+ BinderServerBuilder .forAddress (listenAddress , binderReceiver )
85
+ .addService (def )
86
+ .securityPolicy (
87
+ ServerSecurityPolicy .newBuilder ()
88
+ .servicePolicy (
89
+ SERVICE_NAME ,
90
+ new AsyncSecurityPolicy () {
91
+ @ Override
92
+ public ListenableFuture <Status > checkAuthorizationAsync (int uid ) {
93
+ SettableFuture <Status > status = SettableFuture .create ();
94
+ statusesToSet .add (status );
95
+ return status ;
96
+ }
97
+ })
98
+ .build ())
99
+ .build ();
100
+ try {
101
+ server .start ();
102
+ } catch (IOException e ) {
103
+ throw new IllegalStateException (e );
104
+ }
75
105
76
- AndroidComponentAddress listenAddress = AndroidComponentAddress .forContext (service );
77
- ScheduledExecutorService executor = service .getExecutor ();
106
+ shadowOf (context )
107
+ .setComponentNameAndServiceForBindServiceForIntent (
108
+ listenAddress .asBindIntent (),
109
+ listenAddress .getComponent (),
110
+ checkNotNull (binderReceiver .get ()));
78
111
channel =
79
112
BinderChannelBuilder .forAddress (listenAddress , context )
80
- .executor (executor )
81
- .scheduledExecutorService (executor )
82
- .offloadExecutor (executor )
83
113
.build ();
84
- idleLoopers ();
85
114
}
86
115
87
116
@ After
88
117
public void tearDown () {
89
118
channel .shutdownNow ();
90
- controller . destroy ();
119
+ server . shutdownNow ();
91
120
}
92
121
93
122
@ Test
94
123
public void testAsyncServerSecurityPolicy_failed_returnsFailureStatus () throws Exception {
95
124
ListenableFuture <Status > status = makeCall ();
96
- service .setSecurityPolicyStatusWhenReady (Status .ALREADY_EXISTS );
97
- idleLoopers ();
125
+ statusesToSet .take ().set (Status .ALREADY_EXISTS );
98
126
99
- assertThat (Futures . getDone ( status ).getCode ()).isEqualTo (Status .Code .ALREADY_EXISTS );
127
+ assertThat (status . get ( ).getCode ()).isEqualTo (Status .Code .ALREADY_EXISTS );
100
128
}
101
129
102
130
@ Test
103
131
public void testAsyncServerSecurityPolicy_failedFuture_failsWithCodeInternal () throws Exception {
104
132
ListenableFuture <Status > status = makeCall ();
105
- service .setSecurityPolicyFailed (new IllegalStateException ("oops" ));
106
- idleLoopers ();
133
+ statusesToSet .take ().setException (new IllegalStateException ("oops" ));
107
134
108
- assertThat (Futures . getDone ( status ).getCode ()).isEqualTo (Status .Code .INTERNAL );
135
+ assertThat (status . get ( ).getCode ()).isEqualTo (Status .Code .INTERNAL );
109
136
}
110
137
111
138
@ Test
112
139
public void testAsyncServerSecurityPolicy_allowed_returnsOkStatus () throws Exception {
113
140
ListenableFuture <Status > status = makeCall ();
114
- service .setSecurityPolicyStatusWhenReady (Status .OK );
115
- idleLoopers ();
141
+ statusesToSet .take ().set (Status .OK );
116
142
117
- assertThat (Futures . getDone ( status ).getCode ()).isEqualTo (Status .Code .OK );
143
+ assertThat (status . get ( ).getCode ()).isEqualTo (Status .Code .OK );
118
144
}
119
145
120
146
private ListenableFuture <Status > makeCall () {
121
- ClientCall <Empty , Empty > call =
122
- channel .newCall (
123
- getMethodDescriptor (), CallOptions .DEFAULT .withExecutor (service .getExecutor ()));
147
+ ClientCall <Empty , Empty > call = channel .newCall (getMethodDescriptor (), CallOptions .DEFAULT );
124
148
ListenableFuture <Empty > responseFuture =
125
149
ClientCalls .futureUnaryCall (call , Empty .getDefaultInstance ());
126
150
127
- idleLoopers ();
128
-
129
151
return Futures .catching (
130
152
Futures .transform (responseFuture , unused -> Status .OK , directExecutor ()),
131
153
StatusRuntimeException .class ,
132
154
StatusRuntimeException ::getStatus ,
133
155
directExecutor ());
134
156
}
135
157
136
- private static void idleLoopers () {
137
- shadowOf (Looper .getMainLooper ()).idle ();
138
- }
139
-
140
158
private static MethodDescriptor <Empty , Empty > getMethodDescriptor () {
141
159
MethodDescriptor .Marshaller <Empty > marshaller =
142
160
ProtoLiteUtils .marshaller (Empty .getDefaultInstance ());
@@ -147,106 +165,4 @@ private static MethodDescriptor<Empty, Empty> getMethodDescriptor() {
147
165
.setSampledToLocalTracing (true )
148
166
.build ();
149
167
}
150
-
151
- private static class SomeService extends LifecycleService {
152
-
153
- private final IBinderReceiver binderReceiver = new IBinderReceiver ();
154
- private final ArrayBlockingQueue <SettableFuture <Status >> statusesToSet =
155
- new ArrayBlockingQueue <>(128 );
156
- private Server server ;
157
- private final ScheduledExecutorService scheduledExecutorService =
158
- new MainThreadScheduledExecutorService ();
159
-
160
- @ Override
161
- public void onCreate () {
162
- super .onCreate ();
163
-
164
- MethodDescriptor <Empty , Empty > methodDesc = getMethodDescriptor ();
165
- ServerCallHandler <Empty , Empty > callHandler =
166
- ServerCalls .asyncUnaryCall (
167
- (req , respObserver ) -> {
168
- respObserver .onNext (req );
169
- respObserver .onCompleted ();
170
- });
171
- ServerMethodDefinition <Empty , Empty > methodDef =
172
- ServerMethodDefinition .create (methodDesc , callHandler );
173
- ServerServiceDefinition def =
174
- ServerServiceDefinition .builder (SERVICE_NAME ).addMethod (methodDef ).build ();
175
-
176
- server =
177
- BinderServerBuilder .forAddress (AndroidComponentAddress .forContext (this ), binderReceiver )
178
- .addService (def )
179
- .securityPolicy (
180
- ServerSecurityPolicy .newBuilder ()
181
- .servicePolicy (
182
- SERVICE_NAME ,
183
- new AsyncSecurityPolicy () {
184
- @ Override
185
- public ListenableFuture <Status > checkAuthorizationAsync (int uid ) {
186
- return Futures .submitAsync (
187
- () -> {
188
- SettableFuture <Status > status = SettableFuture .create ();
189
- statusesToSet .add (status );
190
- return status ;
191
- },
192
- getExecutor ());
193
- }
194
- })
195
- .build ())
196
- .executor (getExecutor ())
197
- .scheduledExecutorService (getExecutor ())
198
- .build ();
199
- try {
200
- server .start ();
201
- } catch (IOException e ) {
202
- throw new IllegalStateException (e );
203
- }
204
-
205
- Application context = ApplicationProvider .getApplicationContext ();
206
- ComponentName componentName = new ComponentName (context , SomeService .class );
207
- shadowOf (context )
208
- .setComponentNameAndServiceForBindService (
209
- componentName , checkNotNull (binderReceiver .get ()));
210
- }
211
-
212
- /**
213
- * Returns an {@link ScheduledExecutorService} under which all of the gRPC computations run. The
214
- * execution of any pending tasks on this executor can be triggered via {@link #idleLoopers()}.
215
- */
216
- ScheduledExecutorService getExecutor () {
217
- return scheduledExecutorService ;
218
- }
219
-
220
- void setSecurityPolicyStatusWhenReady (Status status ) {
221
- getNextEnqueuedStatus ().set (status );
222
- }
223
-
224
- void setSecurityPolicyFailed (Exception e ) {
225
- getNextEnqueuedStatus ().setException (e );
226
- }
227
-
228
- private SettableFuture <Status > getNextEnqueuedStatus () {
229
- @ Nullable SettableFuture <Status > future = statusesToSet .poll ();
230
- while (future == null ) {
231
- // Keep idling until the future is available.
232
- idleLoopers ();
233
- future = statusesToSet .poll ();
234
- }
235
- return checkNotNull (future );
236
- }
237
-
238
- @ Override
239
- public IBinder onBind (Intent intent ) {
240
- super .onBind (intent );
241
- return checkNotNull (binderReceiver .get ());
242
- }
243
-
244
- @ Override
245
- public void onDestroy () {
246
- super .onDestroy ();
247
- server .shutdownNow ();
248
- }
249
-
250
- /** A future representing a task submitted to a {@link Handler}. */
251
- }
252
168
}
0 commit comments