2222import static org .robolectric .Shadows .shadowOf ;
2323
2424import 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 ;
3025import androidx .test .core .app .ApplicationProvider ;
3126import com .google .common .util .concurrent .Futures ;
3227import com .google .common .util .concurrent .ListenableFuture ;
4237import io .grpc .ServerServiceDefinition ;
4338import io .grpc .Status ;
4439import io .grpc .StatusRuntimeException ;
45- import io .grpc .binder .internal .MainThreadScheduledExecutorService ;
4640import io .grpc .protobuf .lite .ProtoLiteUtils ;
4741import io .grpc .stub .ClientCalls ;
4842import io .grpc .stub .ServerCalls ;
4943import java .io .IOException ;
5044import java .util .concurrent .ArrayBlockingQueue ;
51- import java .util .concurrent .ScheduledExecutorService ;
52- import javax .annotation .Nullable ;
5345import org .junit .After ;
5446import org .junit .Before ;
5547import org .junit .Test ;
5648import org .junit .runner .RunWith ;
57- import org .robolectric .Robolectric ;
5849import org .robolectric .RobolectricTestRunner ;
59- import org .robolectric .android .controller .ServiceController ;
50+ import org .robolectric .annotation .LooperMode ;
51+ import org .robolectric .annotation .LooperMode .Mode ;
6052
6153@ RunWith (RobolectricTestRunner .class )
54+ @ LooperMode (Mode .INSTRUMENTATION_TEST )
6255public final class RobolectricBinderSecurityTest {
6356
6457 private static final String SERVICE_NAME = "fake_service" ;
6558 private static final String FULL_METHOD_NAME = "fake_service/fake_method" ;
6659 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 ) ;
6962 private ManagedChannel channel ;
63+ private Server server ;
7064
7165 @ Before
7266 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+ }
75105
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 ()));
78111 channel =
79112 BinderChannelBuilder .forAddress (listenAddress , context )
80- .executor (executor )
81- .scheduledExecutorService (executor )
82- .offloadExecutor (executor )
83113 .build ();
84- idleLoopers ();
85114 }
86115
87116 @ After
88117 public void tearDown () {
89118 channel .shutdownNow ();
90- controller . destroy ();
119+ server . shutdownNow ();
91120 }
92121
93122 @ Test
94123 public void testAsyncServerSecurityPolicy_failed_returnsFailureStatus () throws Exception {
95124 ListenableFuture <Status > status = makeCall ();
96- service .setSecurityPolicyStatusWhenReady (Status .ALREADY_EXISTS );
97- idleLoopers ();
125+ statusesToSet .take ().set (Status .ALREADY_EXISTS );
98126
99- assertThat (Futures . getDone ( status ).getCode ()).isEqualTo (Status .Code .ALREADY_EXISTS );
127+ assertThat (status . get ( ).getCode ()).isEqualTo (Status .Code .ALREADY_EXISTS );
100128 }
101129
102130 @ Test
103131 public void testAsyncServerSecurityPolicy_failedFuture_failsWithCodeInternal () throws Exception {
104132 ListenableFuture <Status > status = makeCall ();
105- service .setSecurityPolicyFailed (new IllegalStateException ("oops" ));
106- idleLoopers ();
133+ statusesToSet .take ().setException (new IllegalStateException ("oops" ));
107134
108- assertThat (Futures . getDone ( status ).getCode ()).isEqualTo (Status .Code .INTERNAL );
135+ assertThat (status . get ( ).getCode ()).isEqualTo (Status .Code .INTERNAL );
109136 }
110137
111138 @ Test
112139 public void testAsyncServerSecurityPolicy_allowed_returnsOkStatus () throws Exception {
113140 ListenableFuture <Status > status = makeCall ();
114- service .setSecurityPolicyStatusWhenReady (Status .OK );
115- idleLoopers ();
141+ statusesToSet .take ().set (Status .OK );
116142
117- assertThat (Futures . getDone ( status ).getCode ()).isEqualTo (Status .Code .OK );
143+ assertThat (status . get ( ).getCode ()).isEqualTo (Status .Code .OK );
118144 }
119145
120146 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 );
124148 ListenableFuture <Empty > responseFuture =
125149 ClientCalls .futureUnaryCall (call , Empty .getDefaultInstance ());
126150
127- idleLoopers ();
128-
129151 return Futures .catching (
130152 Futures .transform (responseFuture , unused -> Status .OK , directExecutor ()),
131153 StatusRuntimeException .class ,
132154 StatusRuntimeException ::getStatus ,
133155 directExecutor ());
134156 }
135157
136- private static void idleLoopers () {
137- shadowOf (Looper .getMainLooper ()).idle ();
138- }
139-
140158 private static MethodDescriptor <Empty , Empty > getMethodDescriptor () {
141159 MethodDescriptor .Marshaller <Empty > marshaller =
142160 ProtoLiteUtils .marshaller (Empty .getDefaultInstance ());
@@ -147,106 +165,4 @@ private static MethodDescriptor<Empty, Empty> getMethodDescriptor() {
147165 .setSampledToLocalTracing (true )
148166 .build ();
149167 }
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- }
252168}
0 commit comments