• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.adservices.ondevicepersonalization;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.assertArrayEquals;
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertThrows;
24 import static org.junit.Assert.assertTrue;
25 
26 import android.adservices.ondevicepersonalization.aidl.IDataAccessService;
27 import android.adservices.ondevicepersonalization.aidl.IDataAccessServiceCallback;
28 import android.adservices.ondevicepersonalization.aidl.IFederatedComputeCallback;
29 import android.adservices.ondevicepersonalization.aidl.IFederatedComputeService;
30 import android.adservices.ondevicepersonalization.aidl.IIsolatedModelService;
31 import android.adservices.ondevicepersonalization.aidl.IIsolatedModelServiceCallback;
32 import android.adservices.ondevicepersonalization.aidl.IIsolatedService;
33 import android.adservices.ondevicepersonalization.aidl.IIsolatedServiceCallback;
34 import android.content.ContentValues;
35 import android.federatedcompute.common.TrainingOptions;
36 import android.net.Uri;
37 import android.os.Bundle;
38 import android.os.OutcomeReceiver;
39 import android.os.ParcelFileDescriptor;
40 import android.os.PersistableBundle;
41 
42 import androidx.test.ext.junit.runners.AndroidJUnit4;
43 import androidx.test.filters.SmallTest;
44 
45 import com.android.ondevicepersonalization.internal.util.ByteArrayParceledSlice;
46 import com.android.ondevicepersonalization.internal.util.PersistableBundleUtils;
47 
48 import org.junit.Before;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 
52 import java.util.ArrayList;
53 import java.util.List;
54 import java.util.concurrent.CountDownLatch;
55 
56 /** Unit Tests of IsolatedService class. */
57 @SmallTest
58 @RunWith(AndroidJUnit4.class)
59 public class IsolatedServiceTest {
60     private static final String EVENT_TYPE_KEY = "event_type";
61     private final TestService mTestService = new TestService();
62     private final CountDownLatch mLatch = new CountDownLatch(1);
63     private IIsolatedService mBinder;
64     private boolean mOnExecuteCalled;
65     private boolean mOnDownloadCalled;
66     private boolean mOnRenderCalled;
67     private boolean mOnEventCalled;
68     private boolean mOnTrainingExampleCalled;
69     private boolean mOnWebTriggerCalled;
70     private Bundle mCallbackResult;
71     private int mCallbackErrorCode;
72     private int mIsolatedServiceErrorCode;
73     private byte[] mSerializedExceptionInfo;
74 
75     @Before
setUp()76     public void setUp() {
77         mTestService.onCreate();
78         mBinder = IIsolatedService.Stub.asInterface(mTestService.onBind(null));
79     }
80 
81     @Test
testServiceThrowsIfOpcodeInvalid()82     public void testServiceThrowsIfOpcodeInvalid() throws Exception {
83         assertThrows(
84                 IllegalArgumentException.class,
85                 () -> {
86                     mBinder.onRequest(9999, new Bundle(), new TestServiceCallback());
87                 });
88     }
89 
90     @Test
testOnExecute()91     public void testOnExecute() throws Exception {
92         PersistableBundle appParams = new PersistableBundle();
93         appParams.putString("x", "y");
94         ExecuteInputParcel input =
95                 new ExecuteInputParcel.Builder()
96                         .setAppPackageName("com.testapp")
97                         .setSerializedAppParams(new ByteArrayParceledSlice(
98                                 PersistableBundleUtils.toByteArray(appParams)))
99                         .build();
100         Bundle params = new Bundle();
101         params.putParcelable(Constants.EXTRA_INPUT, input);
102         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
103         params.putBinder(
104                 Constants.EXTRA_FEDERATED_COMPUTE_SERVICE_BINDER,
105                 new TestFederatedComputeService());
106         params.putBinder(Constants.EXTRA_MODEL_SERVICE_BINDER, new TestIsolatedModelService());
107         mBinder.onRequest(Constants.OP_EXECUTE, params, new TestServiceCallback());
108         mLatch.await();
109         assertTrue(mOnExecuteCalled);
110         ExecuteOutputParcel result =
111                 mCallbackResult.getParcelable(Constants.EXTRA_RESULT, ExecuteOutputParcel.class);
112         assertEquals(5, result.getRequestLogRecord().getRows().get(0).getAsInteger("a").intValue());
113         assertEquals("123", result.getRenderingConfig().getKeys().get(0));
114     }
115 
116     @Test
testOnExecutePropagatesError()117     public void testOnExecutePropagatesError() throws Exception {
118         PersistableBundle appParams = new PersistableBundle();
119         appParams.putInt("error", 1); // Trigger an error in the service.
120         ExecuteInputParcel input =
121                 new ExecuteInputParcel.Builder()
122                         .setAppPackageName("com.testapp")
123                         .setSerializedAppParams(new ByteArrayParceledSlice(
124                                 PersistableBundleUtils.toByteArray(appParams)))
125                         .build();
126         Bundle params = new Bundle();
127         params.putParcelable(Constants.EXTRA_INPUT, input);
128         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
129         params.putBinder(
130                 Constants.EXTRA_FEDERATED_COMPUTE_SERVICE_BINDER,
131                 new TestFederatedComputeService());
132         params.putBinder(Constants.EXTRA_MODEL_SERVICE_BINDER, new TestIsolatedModelService());
133         mBinder.onRequest(Constants.OP_EXECUTE, params, new TestServiceCallback());
134         mLatch.await();
135         assertTrue(mOnExecuteCalled);
136         assertEquals(Constants.STATUS_SERVICE_FAILED, mCallbackErrorCode);
137         assertEquals(1, mIsolatedServiceErrorCode);
138     }
139 
140     @Test
testOnExecuteWithoutAppParams()141     public void testOnExecuteWithoutAppParams() throws Exception {
142         ExecuteInputParcel input = new ExecuteInputParcel.Builder().setAppPackageName("com.testapp").build();
143         Bundle params = new Bundle();
144         params.putParcelable(Constants.EXTRA_INPUT, input);
145         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
146         params.putBinder(
147                 Constants.EXTRA_FEDERATED_COMPUTE_SERVICE_BINDER,
148                 new TestFederatedComputeService());
149         params.putBinder(Constants.EXTRA_MODEL_SERVICE_BINDER, new TestIsolatedModelService());
150         mBinder.onRequest(Constants.OP_EXECUTE, params, new TestServiceCallback());
151         mLatch.await();
152         assertTrue(mOnExecuteCalled);
153     }
154 
155     @Test
testOnExecuteThrowsIfParamsMissing()156     public void testOnExecuteThrowsIfParamsMissing() throws Exception {
157         assertThrows(
158                 NullPointerException.class,
159                 () -> {
160                     mBinder.onRequest(Constants.OP_EXECUTE, null, new TestServiceCallback());
161                 });
162     }
163 
164     @Test
testOnExecuteThrowsIfInputMissing()165     public void testOnExecuteThrowsIfInputMissing() throws Exception {
166         Bundle params = new Bundle();
167         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
168         params.putBinder(
169                 Constants.EXTRA_FEDERATED_COMPUTE_SERVICE_BINDER,
170                 new TestFederatedComputeService());
171         mBinder.onRequest(Constants.OP_EXECUTE, params, new TestServiceCallback());
172         mLatch.await();
173         assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode);
174     }
175 
176     @Test
testOnExecuteThrowsIfDataAccessServiceMissing()177     public void testOnExecuteThrowsIfDataAccessServiceMissing() throws Exception {
178         ExecuteInputParcel input = new ExecuteInputParcel.Builder().setAppPackageName("com.testapp").build();
179         Bundle params = new Bundle();
180         params.putBinder(
181                 Constants.EXTRA_FEDERATED_COMPUTE_SERVICE_BINDER,
182                 new TestFederatedComputeService());
183         params.putParcelable(Constants.EXTRA_INPUT, input);
184         mBinder.onRequest(Constants.OP_EXECUTE, params, new TestServiceCallback());
185         mLatch.await();
186         assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode);
187     }
188 
189     @Test
testOnExecuteThrowsIfFederatedComputeServiceMissing()190     public void testOnExecuteThrowsIfFederatedComputeServiceMissing() throws Exception {
191         ExecuteInputParcel input = new ExecuteInputParcel.Builder().setAppPackageName("com.testapp").build();
192         Bundle params = new Bundle();
193         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
194         params.putParcelable(Constants.EXTRA_INPUT, input);
195         mBinder.onRequest(Constants.OP_EXECUTE, params, new TestServiceCallback());
196         mLatch.await();
197         assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode);
198     }
199 
200     @Test
testOnExecuteThrowsIfCallbackMissing()201     public void testOnExecuteThrowsIfCallbackMissing() throws Exception {
202         ExecuteInputParcel input = new ExecuteInputParcel.Builder().setAppPackageName("com.testapp").build();
203         Bundle params = new Bundle();
204         params.putParcelable(Constants.EXTRA_INPUT, input);
205         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
206         assertThrows(
207                 NullPointerException.class,
208                 () -> {
209                     mBinder.onRequest(Constants.OP_EXECUTE, params, null);
210                 });
211     }
212 
213     @Test
testOnDownload()214     public void testOnDownload() throws Exception {
215         DownloadInputParcel input =
216                 new DownloadInputParcel.Builder()
217                         .setDataAccessServiceBinder(new TestDataAccessService())
218                         .build();
219         Bundle params = new Bundle();
220         params.putParcelable(Constants.EXTRA_INPUT, input);
221         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
222         params.putBinder(
223                 Constants.EXTRA_FEDERATED_COMPUTE_SERVICE_BINDER,
224                 new TestFederatedComputeService());
225         mBinder.onRequest(Constants.OP_DOWNLOAD, params, new TestServiceCallback());
226         mLatch.await();
227         assertTrue(mOnDownloadCalled);
228         DownloadCompletedOutputParcel result =
229                 mCallbackResult.getParcelable(
230                         Constants.EXTRA_RESULT, DownloadCompletedOutputParcel.class);
231         assertEquals("12", result.getRetainedKeys().get(0));
232     }
233 
234     @Test
testOnDownloadThrowsIfParamsMissing()235     public void testOnDownloadThrowsIfParamsMissing() throws Exception {
236         assertThrows(
237                 NullPointerException.class,
238                 () -> {
239                     mBinder.onRequest(Constants.OP_DOWNLOAD, null, new TestServiceCallback());
240                 });
241     }
242 
243     @Test
testOnDownloadThrowsIfInputMissing()244     public void testOnDownloadThrowsIfInputMissing() throws Exception {
245         Bundle params = new Bundle();
246         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
247         mBinder.onRequest(Constants.OP_DOWNLOAD, params, new TestServiceCallback());
248         mLatch.await();
249         assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode);
250     }
251 
252     @Test
testOnDownloadThrowsIfDataAccessServiceMissing()253     public void testOnDownloadThrowsIfDataAccessServiceMissing() throws Exception {
254         DownloadInputParcel input =
255                 new DownloadInputParcel.Builder()
256                         .setDataAccessServiceBinder(new TestDataAccessService())
257                         .build();
258         Bundle params = new Bundle();
259         params.putParcelable(Constants.EXTRA_INPUT, input);
260         mBinder.onRequest(Constants.OP_DOWNLOAD, params, new TestServiceCallback());
261         mLatch.await();
262         assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode);
263     }
264 
265     @Test
testOnDownloadThrowsIfFederatedComputeServiceMissing()266     public void testOnDownloadThrowsIfFederatedComputeServiceMissing() throws Exception {
267         DownloadInputParcel input =
268                 new DownloadInputParcel.Builder()
269                         .setDataAccessServiceBinder(new TestDataAccessService())
270                         .build();
271         Bundle params = new Bundle();
272         params.putParcelable(Constants.EXTRA_INPUT, input);
273         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
274         mBinder.onRequest(Constants.OP_DOWNLOAD, params, new TestServiceCallback());
275         mLatch.await();
276         assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode);
277     }
278 
279     @Test
testOnDownloadThrowsIfCallbackMissing()280     public void testOnDownloadThrowsIfCallbackMissing() throws Exception {
281         ParcelFileDescriptor[] pfds = ParcelFileDescriptor.createPipe();
282         DownloadInputParcel input =
283                 new DownloadInputParcel.Builder()
284                         .setDataAccessServiceBinder(new TestDataAccessService())
285                         .build();
286         Bundle params = new Bundle();
287         params.putParcelable(Constants.EXTRA_INPUT, input);
288         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
289         assertThrows(
290                 NullPointerException.class,
291                 () -> {
292                     mBinder.onRequest(Constants.OP_DOWNLOAD, params, null);
293                 });
294     }
295 
296     @Test
testOnRender()297     public void testOnRender() throws Exception {
298         RenderInputParcel input =
299                 new RenderInputParcel.Builder()
300                         .setRenderingConfig(
301                                 new RenderingConfig.Builder().addKey("a").addKey("b").build())
302                         .build();
303         Bundle params = new Bundle();
304         params.putParcelable(Constants.EXTRA_INPUT, input);
305         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
306         mBinder.onRequest(Constants.OP_RENDER, params, new TestServiceCallback());
307         mLatch.await();
308         assertTrue(mOnRenderCalled);
309         RenderOutputParcel result =
310                 mCallbackResult.getParcelable(Constants.EXTRA_RESULT, RenderOutputParcel.class);
311         assertEquals("htmlstring", result.getContent());
312     }
313 
314     @Test
testOnRenderPropagatesError()315     public void testOnRenderPropagatesError() throws Exception {
316         RenderInputParcel input =
317                 new RenderInputParcel.Builder()
318                         .setRenderingConfig(
319                                 new RenderingConfig.Builder()
320                                         .addKey("z") // Trigger error in service.
321                                         .build())
322                         .build();
323         Bundle params = new Bundle();
324         params.putParcelable(Constants.EXTRA_INPUT, input);
325         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
326         mBinder.onRequest(Constants.OP_RENDER, params, new TestServiceCallback());
327         mLatch.await();
328         assertTrue(mOnRenderCalled);
329         assertEquals(Constants.STATUS_SERVICE_FAILED, mCallbackErrorCode);
330     }
331 
332     @Test
testOnRenderThrowsIfParamsMissing()333     public void testOnRenderThrowsIfParamsMissing() throws Exception {
334         assertThrows(
335                 NullPointerException.class,
336                 () -> {
337                     mBinder.onRequest(Constants.OP_RENDER, null, new TestServiceCallback());
338                 });
339     }
340 
341     @Test
testOnRenderThrowsIfInputMissing()342     public void testOnRenderThrowsIfInputMissing() throws Exception {
343         Bundle params = new Bundle();
344         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
345         mBinder.onRequest(Constants.OP_RENDER, params, new TestServiceCallback());
346         mLatch.await();
347         assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode);
348     }
349 
350     @Test
testOnRenderThrowsIfDataAccessServiceMissing()351     public void testOnRenderThrowsIfDataAccessServiceMissing() throws Exception {
352         RenderInputParcel input =
353                 new RenderInputParcel.Builder()
354                         .setRenderingConfig(
355                                 new RenderingConfig.Builder().addKey("a").addKey("b").build())
356                         .build();
357         Bundle params = new Bundle();
358         params.putParcelable(Constants.EXTRA_INPUT, input);
359         mBinder.onRequest(Constants.OP_RENDER, params, new TestServiceCallback());
360         mLatch.await();
361         assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode);
362     }
363 
364     @Test
testOnRenderThrowsIfCallbackMissing()365     public void testOnRenderThrowsIfCallbackMissing() throws Exception {
366         RenderInputParcel input =
367                 new RenderInputParcel.Builder()
368                         .setRenderingConfig(
369                                 new RenderingConfig.Builder().addKey("a").addKey("b").build())
370                         .build();
371         Bundle params = new Bundle();
372         params.putParcelable(Constants.EXTRA_INPUT, input);
373         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
374         assertThrows(
375                 NullPointerException.class,
376                 () -> {
377                     mBinder.onRequest(Constants.OP_RENDER, params, null);
378                 });
379     }
380 
381     @Test
testOnEvent()382     public void testOnEvent() throws Exception {
383         Bundle params = new Bundle();
384         params.putParcelable(
385                 Constants.EXTRA_INPUT,
386                 new EventInputParcel.Builder().setParameters(PersistableBundle.EMPTY).build());
387         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
388         params.putBinder(Constants.EXTRA_MODEL_SERVICE_BINDER, new TestIsolatedModelService());
389         mBinder.onRequest(Constants.OP_WEB_VIEW_EVENT, params, new TestServiceCallback());
390         mLatch.await();
391         assertTrue(mOnEventCalled);
392         EventOutputParcel result =
393                 mCallbackResult.getParcelable(Constants.EXTRA_RESULT, EventOutputParcel.class);
394         assertEquals(1, result.getEventLogRecord().getType());
395         assertEquals(2, result.getEventLogRecord().getRowIndex());
396     }
397 
398     @Test
testOnEventPropagatesError()399     public void testOnEventPropagatesError() throws Exception {
400         PersistableBundle eventParams = new PersistableBundle();
401         // Input value 9999 will trigger an error in the mock service.
402         eventParams.putInt(EVENT_TYPE_KEY, 9999);
403         Bundle params = new Bundle();
404         params.putParcelable(
405                 Constants.EXTRA_INPUT,
406                 new EventInputParcel.Builder().setParameters(eventParams).build());
407         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
408         params.putBinder(Constants.EXTRA_MODEL_SERVICE_BINDER, new TestIsolatedModelService());
409         mBinder.onRequest(Constants.OP_WEB_VIEW_EVENT, params, new TestServiceCallback());
410         mLatch.await();
411         assertTrue(mOnEventCalled);
412         assertEquals(Constants.STATUS_SERVICE_FAILED, mCallbackErrorCode);
413     }
414 
415     @Test
testOnEventThrowsIfParamsMissing()416     public void testOnEventThrowsIfParamsMissing() throws Exception {
417         assertThrows(
418                 NullPointerException.class,
419                 () -> {
420                     mBinder.onRequest(Constants.OP_WEB_VIEW_EVENT, null, new TestServiceCallback());
421                 });
422     }
423 
424     @Test
testOnEventThrowsIfInputMissing()425     public void testOnEventThrowsIfInputMissing() throws Exception {
426         Bundle params = new Bundle();
427         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
428         mBinder.onRequest(Constants.OP_WEB_VIEW_EVENT, params, new TestServiceCallback());
429         mLatch.await();
430         assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode);
431     }
432 
433     @Test
testOnEventThrowsIfDataAccessServiceMissing()434     public void testOnEventThrowsIfDataAccessServiceMissing() throws Exception {
435         Bundle params = new Bundle();
436         params.putParcelable(
437                 Constants.EXTRA_INPUT,
438                 new EventInputParcel.Builder().setParameters(PersistableBundle.EMPTY).build());
439         mBinder.onRequest(Constants.OP_WEB_VIEW_EVENT, params, new TestServiceCallback());
440         mLatch.await();
441         assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode);
442     }
443 
444     @Test
testOnEventThrowsIfCallbackMissing()445     public void testOnEventThrowsIfCallbackMissing() throws Exception {
446         Bundle params = new Bundle();
447         params.putParcelable(
448                 Constants.EXTRA_INPUT,
449                 new EventInputParcel.Builder().setParameters(PersistableBundle.EMPTY).build());
450         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
451         assertThrows(
452                 NullPointerException.class,
453                 () -> {
454                     mBinder.onRequest(Constants.OP_WEB_VIEW_EVENT, params, null);
455                 });
456     }
457 
458     @Test
testOnTrainingExamples()459     public void testOnTrainingExamples() throws Exception {
460         JoinedLogRecord joinedLogRecord = new JoinedLogRecord.Builder().build();
461         TrainingExamplesInputParcel input =
462                 new TrainingExamplesInputParcel.Builder()
463                         .setPopulationName("")
464                         .setTaskName("")
465                         .setResumptionToken(new byte[] {0})
466                         .build();
467         Bundle params = new Bundle();
468         params.putParcelable(Constants.EXTRA_INPUT, input);
469         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
470         mBinder.onRequest(Constants.OP_TRAINING_EXAMPLE, params, new TestServiceCallback());
471         mLatch.await();
472         assertTrue(mOnTrainingExampleCalled);
473         TrainingExamplesOutputParcel result =
474                 mCallbackResult.getParcelable(
475                         Constants.EXTRA_RESULT, TrainingExamplesOutputParcel.class);
476         List<TrainingExampleRecord> examples = result.getTrainingExampleRecords().getList();
477         assertThat(examples).hasSize(1);
478         assertArrayEquals(new byte[] {12}, examples.get(0).getTrainingExample());
479         assertArrayEquals(new byte[] {13}, examples.get(0).getResumptionToken());
480     }
481 
482     @Test
testOnTrainingExampleThrowsIfParamsMissing()483     public void testOnTrainingExampleThrowsIfParamsMissing() throws Exception {
484         assertThrows(
485                 NullPointerException.class,
486                 () -> {
487                     mBinder.onRequest(
488                             Constants.OP_TRAINING_EXAMPLE, null, new TestServiceCallback());
489                 });
490     }
491 
492     @Test
testOnTrainingExampleThrowsIfDataAccessServiceMissing()493     public void testOnTrainingExampleThrowsIfDataAccessServiceMissing() throws Exception {
494         JoinedLogRecord joinedLogRecord = new JoinedLogRecord.Builder().build();
495         TrainingExamplesInputParcel input =
496                 new TrainingExamplesInputParcel.Builder()
497                         .setPopulationName("")
498                         .setTaskName("")
499                         .setResumptionToken(new byte[] {0})
500                         .build();
501         Bundle params = new Bundle();
502         params.putParcelable(Constants.EXTRA_INPUT, input);
503         mBinder.onRequest(Constants.OP_TRAINING_EXAMPLE, params, new TestServiceCallback());
504         mLatch.await();
505         assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode);
506     }
507 
508     @Test
testOnTrainingExampleThrowsIfCallbackMissing()509     public void testOnTrainingExampleThrowsIfCallbackMissing() throws Exception {
510         JoinedLogRecord joinedLogRecord = new JoinedLogRecord.Builder().build();
511         TrainingExamplesInputParcel input =
512                 new TrainingExamplesInputParcel.Builder()
513                         .setPopulationName("")
514                         .setTaskName("")
515                         .setResumptionToken(new byte[] {0})
516                         .build();
517         Bundle params = new Bundle();
518         params.putParcelable(Constants.EXTRA_INPUT, input);
519         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
520         mBinder.onRequest(Constants.OP_TRAINING_EXAMPLE, params, new TestServiceCallback());
521         assertThrows(
522                 NullPointerException.class,
523                 () -> {
524                     mBinder.onRequest(Constants.OP_TRAINING_EXAMPLE, params, null);
525                 });
526     }
527 
528     @Test
testOnWebTrigger()529     public void testOnWebTrigger() throws Exception {
530         WebTriggerInputParcel input =
531                 new WebTriggerInputParcel.Builder(
532                         Uri.parse("http://desturl"),
533                         "com.browser",
534                         new byte[] {1, 2, 3})
535                     .build();
536         Bundle params = new Bundle();
537         params.putParcelable(Constants.EXTRA_INPUT, input);
538         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
539         params.putBinder(Constants.EXTRA_MODEL_SERVICE_BINDER, new TestIsolatedModelService());
540         mBinder.onRequest(Constants.OP_WEB_TRIGGER, params, new TestServiceCallback());
541         mLatch.await();
542         assertTrue(mOnWebTriggerCalled);
543         WebTriggerOutputParcel result =
544                 mCallbackResult.getParcelable(Constants.EXTRA_RESULT, WebTriggerOutputParcel.class);
545         assertEquals(5, result.getRequestLogRecord().getRows().get(0).getAsInteger("a").intValue());
546     }
547 
548     @Test
testOnWebTriggerPropagatesError()549     public void testOnWebTriggerPropagatesError() throws Exception {
550         WebTriggerInputParcel input =
551                 new WebTriggerInputParcel.Builder(
552                         Uri.parse("http://error"),
553                         "com.browser",
554                         new byte[] {1, 2, 3})
555                     .build();
556         Bundle params = new Bundle();
557         params.putParcelable(Constants.EXTRA_INPUT, input);
558         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
559         params.putBinder(Constants.EXTRA_MODEL_SERVICE_BINDER, new TestIsolatedModelService());
560         mBinder.onRequest(Constants.OP_WEB_TRIGGER, params, new TestServiceCallback());
561         mLatch.await();
562         assertTrue(mOnWebTriggerCalled);
563         assertEquals(Constants.STATUS_SERVICE_FAILED, mCallbackErrorCode);
564     }
565 
566     @Test
testOnWebTriggerThrowsIfParamsMissing()567     public void testOnWebTriggerThrowsIfParamsMissing() throws Exception {
568         assertThrows(
569                 NullPointerException.class,
570                 () -> {
571                     mBinder.onRequest(Constants.OP_WEB_TRIGGER, null, new TestServiceCallback());
572                 });
573     }
574 
575     @Test
testOnWebTriggerThrowsIfInputMissing()576     public void testOnWebTriggerThrowsIfInputMissing() throws Exception {
577         Bundle params = new Bundle();
578         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
579         mBinder.onRequest(Constants.OP_WEB_TRIGGER, params, new TestServiceCallback());
580         mLatch.await();
581         assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode);
582     }
583 
584     @Test
testOnWebTriggerThrowsIfDataAccessServiceMissing()585     public void testOnWebTriggerThrowsIfDataAccessServiceMissing() throws Exception {
586         WebTriggerInputParcel input =
587                 new WebTriggerInputParcel.Builder(
588                         Uri.parse("http://desturl"),
589                         "com.browser",
590                         new byte[] {1, 2, 3})
591                     .build();
592         Bundle params = new Bundle();
593         params.putParcelable(Constants.EXTRA_INPUT, input);
594         mBinder.onRequest(Constants.OP_WEB_TRIGGER, params, new TestServiceCallback());
595         mLatch.await();
596         assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode);
597     }
598 
599     @Test
testOnWebTriggerThrowsIfCallbackMissing()600     public void testOnWebTriggerThrowsIfCallbackMissing() throws Exception {
601         WebTriggerInputParcel input =
602                 new WebTriggerInputParcel.Builder(
603                         Uri.parse("http://desturl"),
604                         "com.browser",
605                         new byte[] {1, 2, 3})
606                     .build();
607         Bundle params = new Bundle();
608         params.putParcelable(Constants.EXTRA_INPUT, input);
609         params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
610         assertThrows(
611                 NullPointerException.class,
612                 () -> {
613                     mBinder.onRequest(Constants.OP_WEB_TRIGGER, params, null);
614                 });
615     }
616 
617     static class TestDataAccessService extends IDataAccessService.Stub {
618         @Override
onRequest(int operation, Bundle params, IDataAccessServiceCallback callback)619         public void onRequest(int operation, Bundle params, IDataAccessServiceCallback callback) {}
620         @Override
logApiCallStats(int apiName, long latencyMillis, int responseCode)621         public void logApiCallStats(int apiName, long latencyMillis, int responseCode) {}
622     }
623 
624     static class TestFederatedComputeService extends IFederatedComputeService.Stub {
625         @Override
schedule(TrainingOptions trainingOptions, IFederatedComputeCallback callback)626         public void schedule(TrainingOptions trainingOptions, IFederatedComputeCallback callback) {}
627 
cancel(String populationName, IFederatedComputeCallback callback)628         public void cancel(String populationName, IFederatedComputeCallback callback) {}
629     }
630 
631     class TestWorker implements IsolatedWorker {
632         @Override
onExecute( ExecuteInput input, OutcomeReceiver<ExecuteOutput, IsolatedServiceException> receiver)633         public void onExecute(
634                 ExecuteInput input,
635                 OutcomeReceiver<ExecuteOutput, IsolatedServiceException> receiver) {
636             mOnExecuteCalled = true;
637             if (input.getAppParams() != null && input.getAppParams().getInt("error") > 0) {
638                 receiver.onError(new IsolatedServiceException(1));
639             } else {
640                 ContentValues row = new ContentValues();
641                 row.put("a", 5);
642                 receiver.onResult(
643                         new ExecuteOutput.Builder()
644                                 .setRequestLogRecord(
645                                         new RequestLogRecord.Builder().addRow(row).build())
646                                 .setRenderingConfig(
647                                         new RenderingConfig.Builder().addKey("123").build())
648                                 .build());
649             }
650         }
651 
652         @Override
onDownloadCompleted( DownloadCompletedInput input, OutcomeReceiver<DownloadCompletedOutput, IsolatedServiceException> receiver)653         public void onDownloadCompleted(
654                 DownloadCompletedInput input,
655                 OutcomeReceiver<DownloadCompletedOutput, IsolatedServiceException> receiver) {
656             mOnDownloadCalled = true;
657             receiver.onResult(new DownloadCompletedOutput.Builder().addRetainedKey("12").build());
658         }
659 
660         @Override
onRender( RenderInput input, OutcomeReceiver<RenderOutput, IsolatedServiceException> receiver)661         public void onRender(
662                 RenderInput input,
663                 OutcomeReceiver<RenderOutput, IsolatedServiceException> receiver) {
664             mOnRenderCalled = true;
665             if (input.getRenderingConfig().getKeys().size() >= 1
666                     && input.getRenderingConfig().getKeys().get(0).equals("z")) {
667                 receiver.onError(new IsolatedServiceException(1));
668             } else {
669                 receiver.onResult(new RenderOutput.Builder().setContent("htmlstring").build());
670             }
671         }
672 
673         @Override
onEvent( EventInput input, OutcomeReceiver<EventOutput, IsolatedServiceException> receiver)674         public void onEvent(
675                 EventInput input,
676                 OutcomeReceiver<EventOutput, IsolatedServiceException> receiver) {
677             mOnEventCalled = true;
678             int eventType = input.getParameters().getInt(EVENT_TYPE_KEY);
679             if (eventType == 9999) {
680                 receiver.onError(new IsolatedServiceException(1));
681             } else {
682                 receiver.onResult(
683                         new EventOutput.Builder()
684                                 .setEventLogRecord(
685                                         new EventLogRecord.Builder()
686                                                 .setType(1)
687                                                 .setRowIndex(2)
688                                                 .setData(new ContentValues())
689                                                 .build())
690                                 .build());
691             }
692         }
693 
694         @Override
onTrainingExamples( TrainingExamplesInput input, OutcomeReceiver<TrainingExamplesOutput, IsolatedServiceException> receiver)695         public void onTrainingExamples(
696                 TrainingExamplesInput input,
697                 OutcomeReceiver<TrainingExamplesOutput, IsolatedServiceException> receiver) {
698             mOnTrainingExampleCalled = true;
699             List<TrainingExampleRecord> exampleRecordList = new ArrayList<>();
700             TrainingExampleRecord record =
701                     new TrainingExampleRecord.Builder()
702                             .setTrainingExample(new byte[] {12})
703                             .setResumptionToken(new byte[] {13})
704                             .build();
705             exampleRecordList.add(record);
706             receiver.onResult(
707                     new TrainingExamplesOutput.Builder()
708                             .setTrainingExampleRecords(exampleRecordList)
709                             .build());
710         }
711 
712         @Override
onWebTrigger( WebTriggerInput input, OutcomeReceiver<WebTriggerOutput, IsolatedServiceException> receiver)713         public void onWebTrigger(
714                 WebTriggerInput input,
715                 OutcomeReceiver<WebTriggerOutput, IsolatedServiceException> receiver) {
716             mOnWebTriggerCalled = true;
717             if (input.getDestinationUrl().toString().equals("http://error")) {
718                 receiver.onError(new IsolatedServiceException(1));
719             } else {
720                 ContentValues row = new ContentValues();
721                 row.put("a", 5);
722                 receiver.onResult(
723                         new WebTriggerOutput.Builder()
724                             .setRequestLogRecord(
725                                     new RequestLogRecord.Builder().addRow(row).build())
726                             .build());
727             }
728         }
729     }
730 
731     class TestService extends IsolatedService {
732         @Override
onRequest(RequestToken token)733         public IsolatedWorker onRequest(RequestToken token) {
734             return new TestWorker();
735         }
736     }
737 
738     class TestServiceCallback extends IIsolatedServiceCallback.Stub {
739         @Override
onSuccess(Bundle result)740         public void onSuccess(Bundle result) {
741             mCallbackResult = result;
742             mLatch.countDown();
743         }
744 
745         @Override
onError( int errorCode, int isolatedServiceErrorCode, byte[] serializedExceptionInfo)746         public void onError(
747                 int errorCode,
748                 int isolatedServiceErrorCode,
749                 byte[] serializedExceptionInfo) {
750             mCallbackErrorCode = errorCode;
751             mIsolatedServiceErrorCode = isolatedServiceErrorCode;
752             mSerializedExceptionInfo = serializedExceptionInfo;
753             mLatch.countDown();
754         }
755     }
756 
757     class TestIsolatedModelService extends IIsolatedModelService.Stub {
758         @Override
runInference(Bundle params, IIsolatedModelServiceCallback callback)759         public void runInference(Bundle params, IIsolatedModelServiceCallback callback) {}
760     }
761 }
762