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