Refactor web trigger flow into service flow. Test: atest Bug: 323988231 Change-Id: I663ffd98b71e77c18a654e88acaf91534912c919
diff --git a/src/com/android/ondevicepersonalization/services/webtrigger/WebTriggerFlow.java b/src/com/android/ondevicepersonalization/services/webtrigger/WebTriggerFlow.java index c7fb2ae..be04a6b 100644 --- a/src/com/android/ondevicepersonalization/services/webtrigger/WebTriggerFlow.java +++ b/src/com/android/ondevicepersonalization/services/webtrigger/WebTriggerFlow.java
@@ -19,11 +19,11 @@ import android.adservices.ondevicepersonalization.Constants; import android.adservices.ondevicepersonalization.MeasurementWebTriggerEventParamsParcel; import android.adservices.ondevicepersonalization.OnDevicePersonalizationPermissions; -import android.adservices.ondevicepersonalization.UserData; import android.adservices.ondevicepersonalization.WebTriggerInputParcel; import android.adservices.ondevicepersonalization.WebTriggerOutputParcel; import android.adservices.ondevicepersonalization.aidl.IIsolatedModelService; import android.annotation.NonNull; +import android.content.ComponentName; import android.content.Context; import android.os.Binder; import android.os.Bundle; @@ -42,6 +42,7 @@ import com.android.ondevicepersonalization.services.process.ProcessRunner; import com.android.ondevicepersonalization.services.process.ProcessRunnerImpl; import com.android.ondevicepersonalization.services.process.SharedIsolatedProcessRunner; +import com.android.ondevicepersonalization.services.serviceflow.ServiceFlow; import com.android.ondevicepersonalization.services.util.Clock; import com.android.ondevicepersonalization.services.util.LogUtils; import com.android.ondevicepersonalization.services.util.MonotonicClock; @@ -60,32 +61,10 @@ /** * Handles a Web Trigger Registration. */ -public class WebTriggerFlow { +public class WebTriggerFlow implements ServiceFlow<WebTriggerOutputParcel> { private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger(); private static final String TAG = "WebTriggerFlow"; private static final String TASK_NAME = "WebTrigger"; - private static final String PACKAGE_NAME_KEY = "package"; - private static final String CLASS_NAME_KEY = "class"; - private static final String CERT_DIGEST_KEY = "cert_digest"; - private static final String DATA_KEY = "data"; - - static class ParsedTriggerHeader { - @NonNull String mPackageName; - @NonNull String mClassName; - @NonNull String mCertDigest; - @NonNull String mData; - - ParsedTriggerHeader( - @NonNull String packageName, - @NonNull String className, - @NonNull String certDigest, - @NonNull String data) { - mPackageName = Objects.requireNonNull(packageName); - mClassName = Objects.requireNonNull(className); - mCertDigest = Objects.requireNonNull(certDigest); - mData = Objects.requireNonNull(data); - } - } @VisibleForTesting static class Injector { @@ -115,8 +94,9 @@ @NonNull private final Bundle mParams; @NonNull private final Context mContext; @NonNull private final Injector mInjector; - @NonNull private IsolatedModelServiceProvider mModelServiceProvider; + private Exception mException; + private MeasurementWebTriggerEventParamsParcel mServiceParcel; public WebTriggerFlow( @NonNull Bundle params, @@ -148,79 +128,42 @@ return Futures.immediateFailedFuture(e); } - return Futures.submitAsync(() -> this.processRequest(), mInjector.getExecutor()); + return Futures.submitAsync(this::processRequest, mInjector.getExecutor()); } private ListenableFuture<Void> processRequest() { try { - MeasurementWebTriggerEventParamsParcel wtparams = - Objects.requireNonNull(mParams.getParcelable( - Constants.EXTRA_MEASUREMENT_WEB_TRIGGER_PARAMS, - MeasurementWebTriggerEventParamsParcel.class)); - Objects.requireNonNull(wtparams.getDestinationUrl()); - Objects.requireNonNull(wtparams.getAppPackageName()); - Objects.requireNonNull(wtparams.getIsolatedService()); - Objects.requireNonNull(wtparams.getIsolatedService().getPackageName()); - Objects.requireNonNull(wtparams.getIsolatedService().getClassName()); - if (wtparams.getDestinationUrl().toString().isBlank() - || wtparams.getAppPackageName().isBlank() - || wtparams.getIsolatedService().getPackageName().isBlank() - || wtparams.getIsolatedService().getClassName().isBlank()) { - return Futures.immediateFailedFuture( - new IllegalArgumentException("Missing required parameters")); + if (!isServiceFlowReady()) { + return Futures.immediateFailedFuture(mException); } - if (wtparams.getCertDigest() != null && !wtparams.getCertDigest().isBlank()) { - String installedPackageCert = PackageUtils.getCertDigest( - mContext, wtparams.getIsolatedService().getPackageName()); - if (!wtparams.getCertDigest().equals(installedPackageCert)) { - sLogger.i(TAG + ": Dropping trigger event due to cert mismatch"); - return Futures.immediateFailedFuture( - new IllegalArgumentException("package cert mismatch")); - } - } - - AppManifestConfig config = Objects.requireNonNull( - AppManifestConfigHelper.getAppManifestConfig( - mContext, wtparams.getIsolatedService().getPackageName())); - if (config == null || !wtparams.getIsolatedService().getClassName().equals( - config.getServiceName())) { - sLogger.d(TAG + ": service class not found"); - return Futures.immediateFailedFuture( - new IllegalStateException("package or class not found")); - } - - ListenableFuture<IsolatedServiceInfo> loadFuture = + ListenableFuture<IsolatedServiceInfo> loadServiceFuture = mInjector.getProcessRunner().loadIsolatedService( - TASK_NAME, wtparams.getIsolatedService()); + TASK_NAME, getService()); - ListenableFuture<Void> resultFuture = - FluentFuture.from(loadFuture).transformAsync( - result -> runIsolatedService(wtparams, result), - mInjector.getExecutor()) - .transform( - result -> result.getParcelable( - Constants.EXTRA_RESULT, WebTriggerOutputParcel.class), - mInjector.getExecutor()) + ListenableFuture<Bundle> runServiceFuture = FluentFuture.from(loadServiceFuture) .transformAsync( - result -> writeToLog(wtparams, result), - mInjector.getExecutor()) - .withTimeout( - mInjector.getFlags().getIsolatedServiceDeadlineSeconds(), - TimeUnit.SECONDS, - mInjector.getScheduledExecutor() - ); + isolatedServiceInfo -> + mInjector.getProcessRunner() + .runIsolatedService( + isolatedServiceInfo, + Constants.OP_WEB_TRIGGER, getServiceParams()), + mInjector.getExecutor()); - var unused = Futures.whenAllComplete(loadFuture, resultFuture) + ListenableFuture<WebTriggerOutputParcel> serviceFlowResultFuture = + getServiceFlowResultFuture(runServiceFuture); + + var unused = Futures.whenAllComplete(loadServiceFuture, serviceFlowResultFuture) .callAsync( () -> { mModelServiceProvider.unBindFromModelService(); return mInjector.getProcessRunner().unloadIsolatedService( - loadFuture.get()); + loadServiceFuture.get()); }, mInjector.getExecutor()); - return resultFuture; + return FluentFuture.from(serviceFlowResultFuture) + .transform(result -> null, mInjector.getExecutor()); } catch (Exception e) { sLogger.e(e, TAG + ": Error"); @@ -228,44 +171,128 @@ } } - private ListenableFuture<Bundle> runIsolatedService( - MeasurementWebTriggerEventParamsParcel wtparams, - IsolatedServiceInfo isolatedServiceInfo) { - sLogger.d(TAG + ": runIsolatedService() started."); + @Override + public boolean isServiceFlowReady() { + try { + mServiceParcel = Objects.requireNonNull( + mParams.getParcelable( + Constants.EXTRA_MEASUREMENT_WEB_TRIGGER_PARAMS, + MeasurementWebTriggerEventParamsParcel.class)); + + Objects.requireNonNull(mServiceParcel.getDestinationUrl()); + Objects.requireNonNull(mServiceParcel.getAppPackageName()); + Objects.requireNonNull(mServiceParcel.getIsolatedService()); + Objects.requireNonNull(mServiceParcel.getIsolatedService().getPackageName()); + Objects.requireNonNull(mServiceParcel.getIsolatedService().getClassName()); + + if (mServiceParcel.getDestinationUrl().toString().isBlank() + || mServiceParcel.getAppPackageName().isBlank() + || mServiceParcel.getIsolatedService().getPackageName().isBlank() + || mServiceParcel.getIsolatedService().getClassName().isBlank()) { + mException = new IllegalArgumentException("Missing required parameters"); + return false; + } + + if (mServiceParcel.getCertDigest() != null + && !mServiceParcel.getCertDigest().isBlank()) { + String installedPackageCert = PackageUtils.getCertDigest( + mContext, mServiceParcel.getIsolatedService().getPackageName()); + if (!mServiceParcel.getCertDigest().equals(installedPackageCert)) { + sLogger.i(TAG + ": Dropping trigger event due to cert mismatch"); + mException = new IllegalArgumentException("package cert mismatch"); + return false; + } + } + + AppManifestConfig config = Objects.requireNonNull( + AppManifestConfigHelper.getAppManifestConfig( + mContext, mServiceParcel.getIsolatedService().getPackageName())); + if (!mServiceParcel.getIsolatedService() + .getClassName() + .equals(config.getServiceName())) { + sLogger.d(TAG + ": service class not found"); + mException = new IllegalStateException("package or class not found"); + return false; + } + + return true; + } catch (Exception e) { + mException = new IllegalStateException("Failed to configure web trigger flow."); + return false; + } + } + + @Override + public ComponentName getService() { + return mServiceParcel.getIsolatedService(); + } + + @Override + public Bundle getServiceParams() { Bundle serviceParams = new Bundle(); - WebTriggerInputParcel input = + serviceParams.putParcelable(Constants.EXTRA_INPUT, new WebTriggerInputParcel.Builder( - wtparams.getDestinationUrl(), wtparams.getAppPackageName(), - wtparams.getEventData()) - .build(); - serviceParams.putParcelable(Constants.EXTRA_INPUT, input); - DataAccessServiceImpl binder = new DataAccessServiceImpl( - isolatedServiceInfo.getComponentName().getPackageName(), - mContext, /* includeLocalData */ true, - /* includeEventData */ true); - serviceParams.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, binder); - UserDataAccessor userDataAccessor = new UserDataAccessor(); - UserData userData = userDataAccessor.getUserData(); + mServiceParcel.getDestinationUrl(), mServiceParcel.getAppPackageName(), + mServiceParcel.getEventData()) + .build()); + serviceParams.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, + new DataAccessServiceImpl( + mServiceParcel.getIsolatedService().getPackageName(), + mContext, /* includeLocalData */ true, + /* includeEventData */ true)); + serviceParams.putParcelable(Constants.EXTRA_USER_DATA, + new UserDataAccessor().getUserData()); + mModelServiceProvider = new IsolatedModelServiceProvider(); IIsolatedModelService modelService = mModelServiceProvider.getModelService(mContext); serviceParams.putBinder(Constants.EXTRA_MODEL_SERVICE_BINDER, modelService.asBinder()); - serviceParams.putParcelable(Constants.EXTRA_USER_DATA, userData); - ListenableFuture<Bundle> result = mInjector.getProcessRunner().runIsolatedService( - isolatedServiceInfo, Constants.OP_WEB_TRIGGER, - serviceParams); - return result; + + return serviceParams; } - private ListenableFuture<Void> writeToLog( + @Override + public void uploadServiceFlowMetrics(ListenableFuture<Bundle> runServiceFuture) {} + + @Override + public ListenableFuture<WebTriggerOutputParcel> getServiceFlowResultFuture( + ListenableFuture<Bundle> runServiceFuture) { + return FluentFuture.from(runServiceFuture) + .transform( + result -> result.getParcelable( + Constants.EXTRA_RESULT, WebTriggerOutputParcel.class), + mInjector.getExecutor()) + .transform( + result -> { + writeToLog(mServiceParcel, result); + return result; + }, + mInjector.getExecutor()) + .withTimeout( + mInjector.getFlags().getIsolatedServiceDeadlineSeconds(), + TimeUnit.SECONDS, + mInjector.getScheduledExecutor() + ); + } + + @Override + public void returnResultThroughCallback( + ListenableFuture<WebTriggerOutputParcel> serviceFlowResultFuture) {} + + @Override + public void cleanUpServiceParams() { + mModelServiceProvider.unBindFromModelService(); + } + + private void writeToLog( MeasurementWebTriggerEventParamsParcel wtparams, WebTriggerOutputParcel result) { sLogger.d(TAG + ": writeToLog() started."); - return FluentFuture.from( - LogUtils.writeLogRecords( - mContext, - wtparams.getIsolatedService().getPackageName(), - result.getRequestLogRecord(), - result.getEventLogRecords())) + var unused = FluentFuture.from( + LogUtils.writeLogRecords( + mContext, + wtparams.getIsolatedService().getPackageName(), + result.getRequestLogRecord(), + result.getEventLogRecords())) .transform(v -> null, MoreExecutors.newDirectExecutorService()); }