/* * Copyright 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.adservices.ondevicepersonalization; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import android.adservices.ondevicepersonalization.aidl.IDataAccessService; import android.adservices.ondevicepersonalization.aidl.IDataAccessServiceCallback; import android.adservices.ondevicepersonalization.aidl.IFederatedComputeCallback; import android.adservices.ondevicepersonalization.aidl.IFederatedComputeService; import android.adservices.ondevicepersonalization.aidl.IIsolatedModelService; import android.adservices.ondevicepersonalization.aidl.IIsolatedModelServiceCallback; import android.adservices.ondevicepersonalization.aidl.IIsolatedService; import android.adservices.ondevicepersonalization.aidl.IIsolatedServiceCallback; import android.content.ContentValues; import android.federatedcompute.common.TrainingOptions; import android.net.Uri; import android.os.Bundle; import android.os.OutcomeReceiver; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.ondevicepersonalization.internal.util.ByteArrayParceledSlice; import com.android.ondevicepersonalization.internal.util.PersistableBundleUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; /** Unit Tests of IsolatedService class. */ @SmallTest @RunWith(AndroidJUnit4.class) public class IsolatedServiceTest { private static final String EVENT_TYPE_KEY = "event_type"; private final TestService mTestService = new TestService(); private final CountDownLatch mLatch = new CountDownLatch(1); private IIsolatedService mBinder; private boolean mOnExecuteCalled; private boolean mOnDownloadCalled; private boolean mOnRenderCalled; private boolean mOnEventCalled; private boolean mOnTrainingExampleCalled; private boolean mOnWebTriggerCalled; private Bundle mCallbackResult; private int mCallbackErrorCode; private int mIsolatedServiceErrorCode; private byte[] mSerializedExceptionInfo; @Before public void setUp() { mTestService.onCreate(); mBinder = IIsolatedService.Stub.asInterface(mTestService.onBind(null)); } @Test public void testServiceThrowsIfOpcodeInvalid() throws Exception { assertThrows( IllegalArgumentException.class, () -> { mBinder.onRequest(9999, new Bundle(), new TestServiceCallback()); }); } @Test public void testOnExecute() throws Exception { PersistableBundle appParams = new PersistableBundle(); appParams.putString("x", "y"); ExecuteInputParcel input = new ExecuteInputParcel.Builder() .setAppPackageName("com.testapp") .setSerializedAppParams(new ByteArrayParceledSlice( PersistableBundleUtils.toByteArray(appParams))) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); params.putBinder( Constants.EXTRA_FEDERATED_COMPUTE_SERVICE_BINDER, new TestFederatedComputeService()); params.putBinder(Constants.EXTRA_MODEL_SERVICE_BINDER, new TestIsolatedModelService()); mBinder.onRequest(Constants.OP_EXECUTE, params, new TestServiceCallback()); mLatch.await(); assertTrue(mOnExecuteCalled); ExecuteOutputParcel result = mCallbackResult.getParcelable(Constants.EXTRA_RESULT, ExecuteOutputParcel.class); assertEquals(5, result.getRequestLogRecord().getRows().get(0).getAsInteger("a").intValue()); assertEquals("123", result.getRenderingConfig().getKeys().get(0)); } @Test public void testOnExecutePropagatesError() throws Exception { PersistableBundle appParams = new PersistableBundle(); appParams.putInt("error", 1); // Trigger an error in the service. ExecuteInputParcel input = new ExecuteInputParcel.Builder() .setAppPackageName("com.testapp") .setSerializedAppParams(new ByteArrayParceledSlice( PersistableBundleUtils.toByteArray(appParams))) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); params.putBinder( Constants.EXTRA_FEDERATED_COMPUTE_SERVICE_BINDER, new TestFederatedComputeService()); params.putBinder(Constants.EXTRA_MODEL_SERVICE_BINDER, new TestIsolatedModelService()); mBinder.onRequest(Constants.OP_EXECUTE, params, new TestServiceCallback()); mLatch.await(); assertTrue(mOnExecuteCalled); assertEquals(Constants.STATUS_SERVICE_FAILED, mCallbackErrorCode); assertEquals(1, mIsolatedServiceErrorCode); } @Test public void testOnExecuteWithoutAppParams() throws Exception { ExecuteInputParcel input = new ExecuteInputParcel.Builder().setAppPackageName("com.testapp").build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); params.putBinder( Constants.EXTRA_FEDERATED_COMPUTE_SERVICE_BINDER, new TestFederatedComputeService()); params.putBinder(Constants.EXTRA_MODEL_SERVICE_BINDER, new TestIsolatedModelService()); mBinder.onRequest(Constants.OP_EXECUTE, params, new TestServiceCallback()); mLatch.await(); assertTrue(mOnExecuteCalled); } @Test public void testOnExecuteThrowsIfParamsMissing() throws Exception { assertThrows( NullPointerException.class, () -> { mBinder.onRequest(Constants.OP_EXECUTE, null, new TestServiceCallback()); }); } @Test public void testOnExecuteThrowsIfInputMissing() throws Exception { Bundle params = new Bundle(); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); params.putBinder( Constants.EXTRA_FEDERATED_COMPUTE_SERVICE_BINDER, new TestFederatedComputeService()); mBinder.onRequest(Constants.OP_EXECUTE, params, new TestServiceCallback()); mLatch.await(); assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode); } @Test public void testOnExecuteThrowsIfDataAccessServiceMissing() throws Exception { ExecuteInputParcel input = new ExecuteInputParcel.Builder().setAppPackageName("com.testapp").build(); Bundle params = new Bundle(); params.putBinder( Constants.EXTRA_FEDERATED_COMPUTE_SERVICE_BINDER, new TestFederatedComputeService()); params.putParcelable(Constants.EXTRA_INPUT, input); mBinder.onRequest(Constants.OP_EXECUTE, params, new TestServiceCallback()); mLatch.await(); assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode); } @Test public void testOnExecuteThrowsIfFederatedComputeServiceMissing() throws Exception { ExecuteInputParcel input = new ExecuteInputParcel.Builder().setAppPackageName("com.testapp").build(); Bundle params = new Bundle(); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); params.putParcelable(Constants.EXTRA_INPUT, input); mBinder.onRequest(Constants.OP_EXECUTE, params, new TestServiceCallback()); mLatch.await(); assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode); } @Test public void testOnExecuteThrowsIfCallbackMissing() throws Exception { ExecuteInputParcel input = new ExecuteInputParcel.Builder().setAppPackageName("com.testapp").build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); assertThrows( NullPointerException.class, () -> { mBinder.onRequest(Constants.OP_EXECUTE, params, null); }); } @Test public void testOnDownload() throws Exception { DownloadInputParcel input = new DownloadInputParcel.Builder() .setDataAccessServiceBinder(new TestDataAccessService()) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); params.putBinder( Constants.EXTRA_FEDERATED_COMPUTE_SERVICE_BINDER, new TestFederatedComputeService()); mBinder.onRequest(Constants.OP_DOWNLOAD, params, new TestServiceCallback()); mLatch.await(); assertTrue(mOnDownloadCalled); DownloadCompletedOutputParcel result = mCallbackResult.getParcelable( Constants.EXTRA_RESULT, DownloadCompletedOutputParcel.class); assertEquals("12", result.getRetainedKeys().get(0)); } @Test public void testOnDownloadThrowsIfParamsMissing() throws Exception { assertThrows( NullPointerException.class, () -> { mBinder.onRequest(Constants.OP_DOWNLOAD, null, new TestServiceCallback()); }); } @Test public void testOnDownloadThrowsIfInputMissing() throws Exception { Bundle params = new Bundle(); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); mBinder.onRequest(Constants.OP_DOWNLOAD, params, new TestServiceCallback()); mLatch.await(); assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode); } @Test public void testOnDownloadThrowsIfDataAccessServiceMissing() throws Exception { DownloadInputParcel input = new DownloadInputParcel.Builder() .setDataAccessServiceBinder(new TestDataAccessService()) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); mBinder.onRequest(Constants.OP_DOWNLOAD, params, new TestServiceCallback()); mLatch.await(); assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode); } @Test public void testOnDownloadThrowsIfFederatedComputeServiceMissing() throws Exception { DownloadInputParcel input = new DownloadInputParcel.Builder() .setDataAccessServiceBinder(new TestDataAccessService()) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); mBinder.onRequest(Constants.OP_DOWNLOAD, params, new TestServiceCallback()); mLatch.await(); assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode); } @Test public void testOnDownloadThrowsIfCallbackMissing() throws Exception { ParcelFileDescriptor[] pfds = ParcelFileDescriptor.createPipe(); DownloadInputParcel input = new DownloadInputParcel.Builder() .setDataAccessServiceBinder(new TestDataAccessService()) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); assertThrows( NullPointerException.class, () -> { mBinder.onRequest(Constants.OP_DOWNLOAD, params, null); }); } @Test public void testOnRender() throws Exception { RenderInputParcel input = new RenderInputParcel.Builder() .setRenderingConfig( new RenderingConfig.Builder().addKey("a").addKey("b").build()) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); mBinder.onRequest(Constants.OP_RENDER, params, new TestServiceCallback()); mLatch.await(); assertTrue(mOnRenderCalled); RenderOutputParcel result = mCallbackResult.getParcelable(Constants.EXTRA_RESULT, RenderOutputParcel.class); assertEquals("htmlstring", result.getContent()); } @Test public void testOnRenderPropagatesError() throws Exception { RenderInputParcel input = new RenderInputParcel.Builder() .setRenderingConfig( new RenderingConfig.Builder() .addKey("z") // Trigger error in service. .build()) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); mBinder.onRequest(Constants.OP_RENDER, params, new TestServiceCallback()); mLatch.await(); assertTrue(mOnRenderCalled); assertEquals(Constants.STATUS_SERVICE_FAILED, mCallbackErrorCode); } @Test public void testOnRenderThrowsIfParamsMissing() throws Exception { assertThrows( NullPointerException.class, () -> { mBinder.onRequest(Constants.OP_RENDER, null, new TestServiceCallback()); }); } @Test public void testOnRenderThrowsIfInputMissing() throws Exception { Bundle params = new Bundle(); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); mBinder.onRequest(Constants.OP_RENDER, params, new TestServiceCallback()); mLatch.await(); assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode); } @Test public void testOnRenderThrowsIfDataAccessServiceMissing() throws Exception { RenderInputParcel input = new RenderInputParcel.Builder() .setRenderingConfig( new RenderingConfig.Builder().addKey("a").addKey("b").build()) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); mBinder.onRequest(Constants.OP_RENDER, params, new TestServiceCallback()); mLatch.await(); assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode); } @Test public void testOnRenderThrowsIfCallbackMissing() throws Exception { RenderInputParcel input = new RenderInputParcel.Builder() .setRenderingConfig( new RenderingConfig.Builder().addKey("a").addKey("b").build()) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); assertThrows( NullPointerException.class, () -> { mBinder.onRequest(Constants.OP_RENDER, params, null); }); } @Test public void testOnEvent() throws Exception { Bundle params = new Bundle(); params.putParcelable( Constants.EXTRA_INPUT, new EventInputParcel.Builder().setParameters(PersistableBundle.EMPTY).build()); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); params.putBinder(Constants.EXTRA_MODEL_SERVICE_BINDER, new TestIsolatedModelService()); mBinder.onRequest(Constants.OP_WEB_VIEW_EVENT, params, new TestServiceCallback()); mLatch.await(); assertTrue(mOnEventCalled); EventOutputParcel result = mCallbackResult.getParcelable(Constants.EXTRA_RESULT, EventOutputParcel.class); assertEquals(1, result.getEventLogRecord().getType()); assertEquals(2, result.getEventLogRecord().getRowIndex()); } @Test public void testOnEventPropagatesError() throws Exception { PersistableBundle eventParams = new PersistableBundle(); // Input value 9999 will trigger an error in the mock service. eventParams.putInt(EVENT_TYPE_KEY, 9999); Bundle params = new Bundle(); params.putParcelable( Constants.EXTRA_INPUT, new EventInputParcel.Builder().setParameters(eventParams).build()); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); params.putBinder(Constants.EXTRA_MODEL_SERVICE_BINDER, new TestIsolatedModelService()); mBinder.onRequest(Constants.OP_WEB_VIEW_EVENT, params, new TestServiceCallback()); mLatch.await(); assertTrue(mOnEventCalled); assertEquals(Constants.STATUS_SERVICE_FAILED, mCallbackErrorCode); } @Test public void testOnEventThrowsIfParamsMissing() throws Exception { assertThrows( NullPointerException.class, () -> { mBinder.onRequest(Constants.OP_WEB_VIEW_EVENT, null, new TestServiceCallback()); }); } @Test public void testOnEventThrowsIfInputMissing() throws Exception { Bundle params = new Bundle(); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); mBinder.onRequest(Constants.OP_WEB_VIEW_EVENT, params, new TestServiceCallback()); mLatch.await(); assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode); } @Test public void testOnEventThrowsIfDataAccessServiceMissing() throws Exception { Bundle params = new Bundle(); params.putParcelable( Constants.EXTRA_INPUT, new EventInputParcel.Builder().setParameters(PersistableBundle.EMPTY).build()); mBinder.onRequest(Constants.OP_WEB_VIEW_EVENT, params, new TestServiceCallback()); mLatch.await(); assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode); } @Test public void testOnEventThrowsIfCallbackMissing() throws Exception { Bundle params = new Bundle(); params.putParcelable( Constants.EXTRA_INPUT, new EventInputParcel.Builder().setParameters(PersistableBundle.EMPTY).build()); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); assertThrows( NullPointerException.class, () -> { mBinder.onRequest(Constants.OP_WEB_VIEW_EVENT, params, null); }); } @Test public void testOnTrainingExamples() throws Exception { JoinedLogRecord joinedLogRecord = new JoinedLogRecord.Builder().build(); TrainingExamplesInputParcel input = new TrainingExamplesInputParcel.Builder() .setPopulationName("") .setTaskName("") .setResumptionToken(new byte[] {0}) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); mBinder.onRequest(Constants.OP_TRAINING_EXAMPLE, params, new TestServiceCallback()); mLatch.await(); assertTrue(mOnTrainingExampleCalled); TrainingExamplesOutputParcel result = mCallbackResult.getParcelable( Constants.EXTRA_RESULT, TrainingExamplesOutputParcel.class); List examples = result.getTrainingExampleRecords().getList(); assertThat(examples).hasSize(1); assertArrayEquals(new byte[] {12}, examples.get(0).getTrainingExample()); assertArrayEquals(new byte[] {13}, examples.get(0).getResumptionToken()); } @Test public void testOnTrainingExampleThrowsIfParamsMissing() throws Exception { assertThrows( NullPointerException.class, () -> { mBinder.onRequest( Constants.OP_TRAINING_EXAMPLE, null, new TestServiceCallback()); }); } @Test public void testOnTrainingExampleThrowsIfDataAccessServiceMissing() throws Exception { JoinedLogRecord joinedLogRecord = new JoinedLogRecord.Builder().build(); TrainingExamplesInputParcel input = new TrainingExamplesInputParcel.Builder() .setPopulationName("") .setTaskName("") .setResumptionToken(new byte[] {0}) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); mBinder.onRequest(Constants.OP_TRAINING_EXAMPLE, params, new TestServiceCallback()); mLatch.await(); assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode); } @Test public void testOnTrainingExampleThrowsIfCallbackMissing() throws Exception { JoinedLogRecord joinedLogRecord = new JoinedLogRecord.Builder().build(); TrainingExamplesInputParcel input = new TrainingExamplesInputParcel.Builder() .setPopulationName("") .setTaskName("") .setResumptionToken(new byte[] {0}) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); mBinder.onRequest(Constants.OP_TRAINING_EXAMPLE, params, new TestServiceCallback()); assertThrows( NullPointerException.class, () -> { mBinder.onRequest(Constants.OP_TRAINING_EXAMPLE, params, null); }); } @Test public void testOnWebTrigger() throws Exception { WebTriggerInputParcel input = new WebTriggerInputParcel.Builder( Uri.parse("http://desturl"), "com.browser", new byte[] {1, 2, 3}) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); params.putBinder(Constants.EXTRA_MODEL_SERVICE_BINDER, new TestIsolatedModelService()); mBinder.onRequest(Constants.OP_WEB_TRIGGER, params, new TestServiceCallback()); mLatch.await(); assertTrue(mOnWebTriggerCalled); WebTriggerOutputParcel result = mCallbackResult.getParcelable(Constants.EXTRA_RESULT, WebTriggerOutputParcel.class); assertEquals(5, result.getRequestLogRecord().getRows().get(0).getAsInteger("a").intValue()); } @Test public void testOnWebTriggerPropagatesError() throws Exception { WebTriggerInputParcel input = new WebTriggerInputParcel.Builder( Uri.parse("http://error"), "com.browser", new byte[] {1, 2, 3}) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); params.putBinder(Constants.EXTRA_MODEL_SERVICE_BINDER, new TestIsolatedModelService()); mBinder.onRequest(Constants.OP_WEB_TRIGGER, params, new TestServiceCallback()); mLatch.await(); assertTrue(mOnWebTriggerCalled); assertEquals(Constants.STATUS_SERVICE_FAILED, mCallbackErrorCode); } @Test public void testOnWebTriggerThrowsIfParamsMissing() throws Exception { assertThrows( NullPointerException.class, () -> { mBinder.onRequest(Constants.OP_WEB_TRIGGER, null, new TestServiceCallback()); }); } @Test public void testOnWebTriggerThrowsIfInputMissing() throws Exception { Bundle params = new Bundle(); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); mBinder.onRequest(Constants.OP_WEB_TRIGGER, params, new TestServiceCallback()); mLatch.await(); assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode); } @Test public void testOnWebTriggerThrowsIfDataAccessServiceMissing() throws Exception { WebTriggerInputParcel input = new WebTriggerInputParcel.Builder( Uri.parse("http://desturl"), "com.browser", new byte[] {1, 2, 3}) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); mBinder.onRequest(Constants.OP_WEB_TRIGGER, params, new TestServiceCallback()); mLatch.await(); assertEquals(Constants.STATUS_INTERNAL_ERROR, mCallbackErrorCode); } @Test public void testOnWebTriggerThrowsIfCallbackMissing() throws Exception { WebTriggerInputParcel input = new WebTriggerInputParcel.Builder( Uri.parse("http://desturl"), "com.browser", new byte[] {1, 2, 3}) .build(); Bundle params = new Bundle(); params.putParcelable(Constants.EXTRA_INPUT, input); params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService()); assertThrows( NullPointerException.class, () -> { mBinder.onRequest(Constants.OP_WEB_TRIGGER, params, null); }); } static class TestDataAccessService extends IDataAccessService.Stub { @Override public void onRequest(int operation, Bundle params, IDataAccessServiceCallback callback) {} @Override public void logApiCallStats(int apiName, long latencyMillis, int responseCode) {} } static class TestFederatedComputeService extends IFederatedComputeService.Stub { @Override public void schedule(TrainingOptions trainingOptions, IFederatedComputeCallback callback) {} public void cancel(String populationName, IFederatedComputeCallback callback) {} } class TestWorker implements IsolatedWorker { @Override public void onExecute( ExecuteInput input, OutcomeReceiver receiver) { mOnExecuteCalled = true; if (input.getAppParams() != null && input.getAppParams().getInt("error") > 0) { receiver.onError(new IsolatedServiceException(1)); } else { ContentValues row = new ContentValues(); row.put("a", 5); receiver.onResult( new ExecuteOutput.Builder() .setRequestLogRecord( new RequestLogRecord.Builder().addRow(row).build()) .setRenderingConfig( new RenderingConfig.Builder().addKey("123").build()) .build()); } } @Override public void onDownloadCompleted( DownloadCompletedInput input, OutcomeReceiver receiver) { mOnDownloadCalled = true; receiver.onResult(new DownloadCompletedOutput.Builder().addRetainedKey("12").build()); } @Override public void onRender( RenderInput input, OutcomeReceiver receiver) { mOnRenderCalled = true; if (input.getRenderingConfig().getKeys().size() >= 1 && input.getRenderingConfig().getKeys().get(0).equals("z")) { receiver.onError(new IsolatedServiceException(1)); } else { receiver.onResult(new RenderOutput.Builder().setContent("htmlstring").build()); } } @Override public void onEvent( EventInput input, OutcomeReceiver receiver) { mOnEventCalled = true; int eventType = input.getParameters().getInt(EVENT_TYPE_KEY); if (eventType == 9999) { receiver.onError(new IsolatedServiceException(1)); } else { receiver.onResult( new EventOutput.Builder() .setEventLogRecord( new EventLogRecord.Builder() .setType(1) .setRowIndex(2) .setData(new ContentValues()) .build()) .build()); } } @Override public void onTrainingExamples( TrainingExamplesInput input, OutcomeReceiver receiver) { mOnTrainingExampleCalled = true; List exampleRecordList = new ArrayList<>(); TrainingExampleRecord record = new TrainingExampleRecord.Builder() .setTrainingExample(new byte[] {12}) .setResumptionToken(new byte[] {13}) .build(); exampleRecordList.add(record); receiver.onResult( new TrainingExamplesOutput.Builder() .setTrainingExampleRecords(exampleRecordList) .build()); } @Override public void onWebTrigger( WebTriggerInput input, OutcomeReceiver receiver) { mOnWebTriggerCalled = true; if (input.getDestinationUrl().toString().equals("http://error")) { receiver.onError(new IsolatedServiceException(1)); } else { ContentValues row = new ContentValues(); row.put("a", 5); receiver.onResult( new WebTriggerOutput.Builder() .setRequestLogRecord( new RequestLogRecord.Builder().addRow(row).build()) .build()); } } } class TestService extends IsolatedService { @Override public IsolatedWorker onRequest(RequestToken token) { return new TestWorker(); } } class TestServiceCallback extends IIsolatedServiceCallback.Stub { @Override public void onSuccess(Bundle result) { mCallbackResult = result; mLatch.countDown(); } @Override public void onError( int errorCode, int isolatedServiceErrorCode, byte[] serializedExceptionInfo) { mCallbackErrorCode = errorCode; mIsolatedServiceErrorCode = isolatedServiceErrorCode; mSerializedExceptionInfo = serializedExceptionInfo; mLatch.countDown(); } } class TestIsolatedModelService extends IIsolatedModelService.Stub { @Override public void runInference(Bundle params, IIsolatedModelServiceCallback callback) {} } }