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