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());
     }