1 /*
2  * Copyright 2024 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 org.junit.Assert.assertArrayEquals;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertNotNull;
23 import static org.junit.Assert.assertTrue;
24 
25 import android.adservices.ondevicepersonalization.OnDevicePersonalizationManager.ExecuteResult;
26 import android.adservices.ondevicepersonalization.aidl.IExecuteCallback;
27 import android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationManagingService;
28 import android.adservices.ondevicepersonalization.aidl.IRegisterMeasurementEventCallback;
29 import android.adservices.ondevicepersonalization.aidl.IRequestSurfacePackageCallback;
30 import android.content.ComponentName;
31 import android.content.Context;
32 import android.os.Bundle;
33 import android.os.IBinder;
34 import android.os.PersistableBundle;
35 import android.os.RemoteException;
36 import android.os.SystemClock;
37 import android.util.Log;
38 
39 import androidx.test.core.app.ApplicationProvider;
40 
41 import com.android.compatibility.common.util.ShellUtils;
42 import com.android.federatedcompute.internal.util.AbstractServiceBinder;
43 import com.android.ondevicepersonalization.internal.util.ByteArrayParceledSlice;
44 import com.android.ondevicepersonalization.internal.util.PersistableBundleUtils;
45 import com.android.ondevicepersonalization.testing.utils.ResultReceiver;
46 
47 import org.junit.Before;
48 import org.junit.Test;
49 import org.junit.runner.RunWith;
50 import org.junit.runners.Parameterized;
51 
52 import java.util.Arrays;
53 import java.util.Collection;
54 import java.util.concurrent.Executor;
55 import java.util.concurrent.Executors;
56 
57 @RunWith(Parameterized.class)
58 public final class OnDevicePersonalizationManagerTest {
59     private static final String TAG = "OnDevicePersonalizationManagerTest";
60     private static final String KEY_OP = "op";
61     private static final String KEY_STATUS_CODE = "status";
62     private static final String KEY_SERVICE_ERROR_CODE = "serviceerror";
63     private static final String KEY_ERROR_MESSAGE = "errormessage";
64     private final Context mContext = ApplicationProvider.getApplicationContext();
65     private final TestServiceBinder mTestBinder = new TestServiceBinder(
66             IOnDevicePersonalizationManagingService.Stub.asInterface(new TestService()));
67     private final OnDevicePersonalizationManager mManager =
68             new OnDevicePersonalizationManager(mContext, mTestBinder);
69     private boolean mLogApiStatsCalled = false;
70 
71     @Parameterized.Parameter(0)
72     public boolean mIsSipFeatureEnabled;
73 
74     @Parameterized.Parameters
data()75     public static Collection<Object[]> data() {
76         return Arrays.asList(
77                 new Object[][] {
78                         {true}, {false}
79                 }
80         );
81     }
82 
83     @Before
setUp()84     public void setUp() {
85         ShellUtils.runShellCommand(
86                 "device_config put on_device_personalization "
87                         + "shared_isolated_process_feature_enabled "
88                         + mIsSipFeatureEnabled);
89     }
90 
91     @Test
testExecuteSuccess()92     public void testExecuteSuccess() throws Exception {
93         PersistableBundle params = new PersistableBundle();
94         params.putString(KEY_OP, "ok");
95         var receiver = new ResultReceiver<ExecuteResult>();
96         mManager.execute(
97                 ComponentName.createRelative("com.example.service", ".Example"),
98                 params,
99                 Executors.newSingleThreadExecutor(),
100                 receiver);
101         assertTrue(receiver.isSuccess());
102         assertFalse(receiver.isError());
103         assertNotNull(receiver.getResult());
104         assertEquals(receiver.getResult().getSurfacePackageToken().getTokenString(), "aaaa");
105         assertArrayEquals(receiver.getResult().getOutputData(), new byte[]{1, 2, 3});
106         assertTrue(mLogApiStatsCalled);
107     }
108 
109     @Test
testExecuteUnknownError()110     public void testExecuteUnknownError() throws Exception {
111         PersistableBundle params = new PersistableBundle();
112         params.putString(KEY_OP, "error");
113         var receiver = new ResultReceiver<ExecuteResult>();
114         mManager.execute(
115                 ComponentName.createRelative("com.example.service", ".Example"),
116                 params,
117                 Executors.newSingleThreadExecutor(),
118                 receiver);
119         assertFalse(receiver.isSuccess());
120         assertTrue(receiver.isError());
121         assertTrue(receiver.getException() instanceof IllegalStateException);
122         assertTrue(mLogApiStatsCalled);
123     }
124 
125     @Test
testExecuteServiceError()126     public void testExecuteServiceError() throws Exception {
127         PersistableBundle params = new PersistableBundle();
128         params.putString(KEY_OP, "error");
129         params.putInt(KEY_STATUS_CODE, Constants.STATUS_SERVICE_FAILED);
130         var receiver = new ResultReceiver<ExecuteResult>();
131         mManager.execute(
132                 ComponentName.createRelative("com.example.service", ".Example"),
133                 params,
134                 Executors.newSingleThreadExecutor(),
135                 receiver);
136         assertFalse(receiver.isSuccess());
137         assertTrue(receiver.isError());
138         assertTrue(receiver.getException() instanceof OnDevicePersonalizationException);
139         assertTrue(mLogApiStatsCalled);
140     }
141 
142     @Test
testExecuteErrorWithCode()143     public void testExecuteErrorWithCode() throws Exception {
144         PersistableBundle params = new PersistableBundle();
145         params.putString(KEY_OP, "error");
146         params.putInt(KEY_STATUS_CODE, Constants.STATUS_SERVICE_FAILED);
147         params.putInt(KEY_SERVICE_ERROR_CODE, 42);
148         var receiver = new ResultReceiver<ExecuteResult>();
149         mManager.execute(
150                 ComponentName.createRelative("com.example.service", ".Example"),
151                 params,
152                 Executors.newSingleThreadExecutor(),
153                 receiver);
154         assertFalse(receiver.isSuccess());
155         assertTrue(receiver.isError());
156         assertTrue(receiver.getException() instanceof OnDevicePersonalizationException);
157         assertEquals(OnDevicePersonalizationException.ERROR_ISOLATED_SERVICE_FAILED,
158                 ((OnDevicePersonalizationException) receiver.getException()).getErrorCode());
159         assertTrue(receiver.getException().getCause() instanceof IsolatedServiceException);
160         assertEquals(42,
161                 ((IsolatedServiceException) receiver.getException().getCause()).getErrorCode());
162         assertTrue(mLogApiStatsCalled);
163     }
164 
165     @Test
testExecuteErrorWithMessage()166     public void testExecuteErrorWithMessage() throws Exception {
167         PersistableBundle params = new PersistableBundle();
168         params.putString(KEY_OP, "error");
169         params.putInt(KEY_STATUS_CODE, Constants.STATUS_SERVICE_FAILED);
170         params.putString(KEY_ERROR_MESSAGE, "TestErrorMessage");
171         var receiver = new ResultReceiver<ExecuteResult>();
172         mManager.execute(
173                 ComponentName.createRelative("com.example.service", ".Example"),
174                 params,
175                 Executors.newSingleThreadExecutor(),
176                 receiver);
177         assertFalse(receiver.isSuccess());
178         assertTrue(receiver.isError());
179         assertEquals("TestErrorMessage", receiver.getException().getMessage());
180         assertTrue(mLogApiStatsCalled);
181     }
182 
183     @Test
testExecuteCatchesIaeFromService()184     public void testExecuteCatchesIaeFromService() throws Exception {
185         PersistableBundle params = new PersistableBundle();
186         params.putString(KEY_OP, "iae");
187         var receiver = new ResultReceiver<ExecuteResult>();
188         mManager.execute(
189                 ComponentName.createRelative("com.example.service", ".Example"),
190                 params,
191                 Executors.newSingleThreadExecutor(),
192                 receiver);
193         assertFalse(receiver.isSuccess());
194         assertTrue(receiver.isError());
195         assertTrue(receiver.getException() instanceof IllegalArgumentException);
196         assertTrue(mLogApiStatsCalled);
197     }
198 
199     @Test
testExecuteCatchesNpeFromService()200     public void testExecuteCatchesNpeFromService() throws Exception {
201         PersistableBundle params = new PersistableBundle();
202         params.putString(KEY_OP, "npe");
203         var receiver = new ResultReceiver<ExecuteResult>();
204         mManager.execute(
205                 ComponentName.createRelative("com.example.service", ".Example"),
206                 params,
207                 Executors.newSingleThreadExecutor(),
208                 receiver);
209         assertFalse(receiver.isSuccess());
210         assertTrue(receiver.isError());
211         assertTrue(receiver.getException() instanceof NullPointerException);
212         assertTrue(mLogApiStatsCalled);
213     }
214 
215     @Test
testExecuteCatchesOtherExceptions()216     public void testExecuteCatchesOtherExceptions() throws Exception {
217         PersistableBundle params = new PersistableBundle();
218         params.putString(KEY_OP, "ise");
219         var receiver = new ResultReceiver<ExecuteResult>();
220         mManager.execute(
221                 ComponentName.createRelative("com.example.service", ".Example"),
222                 params,
223                 Executors.newSingleThreadExecutor(),
224                 receiver);
225         assertFalse(receiver.isSuccess());
226         assertTrue(receiver.isError());
227         assertTrue(receiver.getException() instanceof IllegalStateException);
228         assertTrue(mLogApiStatsCalled);
229     }
230 
231     class TestService extends IOnDevicePersonalizationManagingService.Stub {
232         @Override
getVersion()233         public String getVersion() {
234             return "1.0";
235         }
236 
237         @Override
execute( String callingPackageName, ComponentName handler, Bundle wrappedParams, CallerMetadata metadata, IExecuteCallback callback)238         public void execute(
239                 String callingPackageName,
240                 ComponentName handler,
241                 Bundle wrappedParams,
242                 CallerMetadata metadata,
243                 IExecuteCallback callback) {
244             try {
245                 PersistableBundle params;
246                 String op;
247                 try {
248                     ByteArrayParceledSlice paramsBuffer = wrappedParams.getParcelable(
249                             Constants.EXTRA_APP_PARAMS_SERIALIZED, ByteArrayParceledSlice.class);
250                     params = PersistableBundleUtils.fromByteArray(paramsBuffer.getByteArray());
251                     op = params.getString(KEY_OP);
252                 } catch (Exception e) {
253                     Log.e(TAG, "error extracting params", e);
254                     throw new IllegalStateException(e);
255                 }
256                 if (op.equals("ok")) {
257                     Bundle bundle = new Bundle();
258                     bundle.putString(Constants.EXTRA_SURFACE_PACKAGE_TOKEN_STRING, "aaaa");
259                     bundle.putByteArray(Constants.EXTRA_OUTPUT_DATA, new byte[]{1, 2, 3});
260                     callback.onSuccess(bundle,
261                             new CalleeMetadata.Builder().setCallbackInvokeTimeMillis(
262                                     SystemClock.elapsedRealtime()).build());
263                 } else if (op.equals("error")) {
264                     int statusCode = params.getInt(KEY_STATUS_CODE,
265                             Constants.STATUS_INTERNAL_ERROR);
266                     int serviceErrorCode = params.getInt(KEY_SERVICE_ERROR_CODE, 0);
267                     String errorMessage = params.getString(KEY_ERROR_MESSAGE);
268                     callback.onError(statusCode, serviceErrorCode, errorMessage,
269                             new CalleeMetadata.Builder().setCallbackInvokeTimeMillis(
270                                     SystemClock.elapsedRealtime()).build());
271                 } else if (op.equals("iae")) {
272                     throw new IllegalArgumentException();
273                 } else if (op.equals("npe")) {
274                     throw new NullPointerException();
275                 } else if (op.equals("ise")) {
276                     throw new IllegalStateException();
277                 } else {
278                     throw new UnsupportedOperationException();
279                 }
280             } catch (RemoteException e) {
281                 Log.e(TAG, "callback error", e);
282             }
283         }
284 
285         @Override
requestSurfacePackage( String surfacePackageToken, IBinder hostToken, int displayId, int width, int height, CallerMetadata metadata, IRequestSurfacePackageCallback callback)286         public void requestSurfacePackage(
287                 String surfacePackageToken,
288                 IBinder hostToken,
289                 int displayId,
290                 int width,
291                 int height,
292                 CallerMetadata metadata,
293                 IRequestSurfacePackageCallback callback) {
294             throw new UnsupportedOperationException();
295         }
296 
297         @Override
registerMeasurementEvent( int eventType, Bundle params, CallerMetadata metadata, IRegisterMeasurementEventCallback callback)298         public void registerMeasurementEvent(
299                 int eventType,
300                 Bundle params,
301                 CallerMetadata metadata,
302                 IRegisterMeasurementEventCallback callback) {
303             throw new UnsupportedOperationException();
304         }
305 
306         @Override
logApiCallStats( String sdkPackageName, int apiName, long latencyMillis, long rpcCallLatencyMillis, long rpcReturnLatencyMillis, int responseCode)307         public void logApiCallStats(
308                 String sdkPackageName,
309                 int apiName,
310                 long latencyMillis,
311                 long rpcCallLatencyMillis,
312                 long rpcReturnLatencyMillis,
313                 int responseCode) {
314             if (!sdkPackageName.equals("com.example.service")) {
315                 return;
316             }
317             mLogApiStatsCalled = true;
318         }
319     }
320 
321     class TestServiceBinder
322             extends AbstractServiceBinder<IOnDevicePersonalizationManagingService> {
323         private final IOnDevicePersonalizationManagingService mService;
TestServiceBinder(IOnDevicePersonalizationManagingService service)324         TestServiceBinder(IOnDevicePersonalizationManagingService service) {
325             mService = service;
326         }
327 
328         @Override
getService(Executor executor)329         public IOnDevicePersonalizationManagingService getService(Executor executor) {
330             return mService;
331         }
332 
333         @Override
unbindFromService()334         public void unbindFromService() {}
335     }
336 }
337