Snap for 11397440 from 318cb24be4e5cb0fb7f34e6a5cbab1d684e418e3 to mainline-ipsec-release

Change-Id: Idb672edbe7b98c506cda42bef78712ceb9c4d323
diff --git a/federatedcompute/src/com/android/federatedcompute/services/common/PhFlags.java b/federatedcompute/src/com/android/federatedcompute/services/common/PhFlags.java
index ba7cf9e..0a3cd75 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/common/PhFlags.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/common/PhFlags.java
@@ -49,6 +49,23 @@
 
     static final String FCP_ENABLE_ENCRYPTION = "fcp_enable_encryption";
 
+    static final String MIN_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION_CONFIG_NAME =
+            "min_scheduling_interval_secs_for_federated_computation";
+
+    static final String MAX_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION_CONFIG_NAME =
+            "max_scheduling_interval_secs_for_federated_computation";
+
+    static final String DEFAULT_SCHEDULING_PERIOD_SECS_CONFIG_NAME =
+            "default_scheduling_period_secs";
+
+    static final String MAX_SCHEDULING_PERIOD_SECS_CONFIG_NAME =
+            "max_scheduling_period_secs";
+
+    static final String TRANSIENT_ERROR_RETRY_DELAY_JITTER_PERCENT_CONFIG_NAME =
+            "transient_error_retry_delay_jitter_percent";
+
+    static final String TRANSIENT_ERROR_RETRY_DELAY_SECS_CONFIG_NAME =
+            "transient_error_retry_delay_secs";
     private static final PhFlags sSingleton = new PhFlags();
 
     /** Returns the singleton instance of the PhFlags. */
@@ -114,4 +131,52 @@
                 /* name= */ FCP_ENABLE_ENCRYPTION,
                 /* defaultValue= */ ENCRYPTION_ENABLED);
     }
+
+    @Override
+    public long getMinSchedulingIntervalSecsForFederatedComputation() {
+        return DeviceConfig.getLong(
+                /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                /* name= */ MIN_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION_CONFIG_NAME,
+                /* defaultValue= */ MIN_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION);
+    }
+
+    @Override
+    public long getMaxSchedulingIntervalSecsForFederatedComputation() {
+        return DeviceConfig.getLong(
+                /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                /* name= */ MAX_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION_CONFIG_NAME,
+                /* defaultValue= */ MAX_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION);
+    }
+
+    @Override
+    public long getDefaultSchedulingPeriodSecs() {
+        return DeviceConfig.getLong(
+                /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                /* name= */ DEFAULT_SCHEDULING_PERIOD_SECS_CONFIG_NAME,
+                /* defaultValue= */ DEFAULT_SCHEDULING_PERIOD_SECS);
+    }
+
+    @Override
+    public long getMaxSchedulingPeriodSecs() {
+        return DeviceConfig.getLong(
+                /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                /* name= */ MAX_SCHEDULING_PERIOD_SECS_CONFIG_NAME,
+                /* defaultValue= */ MAX_SCHEDULING_PERIOD_SECS);
+    }
+
+    @Override
+    public float getTransientErrorRetryDelayJitterPercent() {
+        return DeviceConfig.getFloat(
+                /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                /* name= */ TRANSIENT_ERROR_RETRY_DELAY_JITTER_PERCENT_CONFIG_NAME,
+                /* defaultValue= */ TRANSIENT_ERROR_RETRY_DELAY_JITTER_PERCENT);
+    }
+
+    @Override
+    public long getTransientErrorRetryDelaySecs() {
+        return DeviceConfig.getLong(
+                /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                /* name= */ TRANSIENT_ERROR_RETRY_DELAY_SECS_CONFIG_NAME,
+                /* defaultValue= */ TRANSIENT_ERROR_RETRY_DELAY_SECS);
+    }
 }
diff --git a/framework/api/current.txt b/framework/api/current.txt
index 7d12fa2..0e6f776 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -204,7 +204,7 @@
   @FlaggedApi("com.android.adservices.ondevicepersonalization.flags.on_device_personalization_apis_enabled") public class SurfacePackageToken {
   }
 
-  @FlaggedApi("enable_ondevicepersonalization_apis") public final class TrainingExampleRecord implements android.os.Parcelable {
+  @FlaggedApi("com.android.adservices.ondevicepersonalization.flags.on_device_personalization_apis_enabled") public final class TrainingExampleRecord implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public byte[] getResumptionToken();
     method @Nullable public byte[] getTrainingExample();
@@ -212,7 +212,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.adservices.ondevicepersonalization.TrainingExampleRecord> CREATOR;
   }
 
-  @FlaggedApi("enable_ondevicepersonalization_apis") public static final class TrainingExampleRecord.Builder {
+  public static final class TrainingExampleRecord.Builder {
     ctor public TrainingExampleRecord.Builder();
     method @NonNull public android.adservices.ondevicepersonalization.TrainingExampleRecord build();
     method @NonNull public android.adservices.ondevicepersonalization.TrainingExampleRecord.Builder setResumptionToken(@NonNull byte...);
diff --git a/framework/java/android/adservices/ondevicepersonalization/Constants.java b/framework/java/android/adservices/ondevicepersonalization/Constants.java
index f720716..9e9d508 100644
--- a/framework/java/android/adservices/ondevicepersonalization/Constants.java
+++ b/framework/java/android/adservices/ondevicepersonalization/Constants.java
@@ -51,13 +51,15 @@
     public static final String EXTRA_LOOKUP_KEYS =
             "android.ondevicepersonalization.extra.lookup_keys";
     public static final String EXTRA_MIME_TYPE = "android.ondevicepersonalization.extra.mime_type";
+    public static final String EXTRA_OUTPUT_DATA =
+            "android.ondevicepersonalization.extra.output_data";
     public static final String EXTRA_RESPONSE_DATA =
             "android.ondevicepersonalization.extra.response_data";
+    public static final String EXTRA_RESULT = "android.ondevicepersonalization.extra.result";
+    public static final String EXTRA_SURFACE_PACKAGE_TOKEN_STRING =
+            "android.ondevicepersonalization.extra.surface_package_token_string";
     public static final String EXTRA_USER_DATA = "android.ondevicepersonalization.extra.user_data";
     public static final String EXTRA_VALUE = "android.ondevicepersonalization.extra.value";
-    public static final String EXTRA_RESULT = "android.ondevicepersonalization.extra.result";
-    public static final String KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS =
-            "enable_ondevicepersonalization_apis";
 
     // Data Access Service operations.
     public static final int DATA_ACCESS_OP_REMOTE_DATA_LOOKUP = 1;
diff --git a/framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.java b/framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.java
index fefa777..57ed941 100644
--- a/framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.java
+++ b/framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.java
@@ -61,6 +61,17 @@
     @DataClass.PluralOf("eventLogRecord")
     @NonNull private List<EventLogRecord> mEventLogRecords = Collections.emptyList();
 
+    /**
+     * A byte array returned by an {@link IsolatedService} to a calling app. The contents of
+     * this array is returned to the caller of
+     * {@link OnDevicePersonalizationManager#execute(ComponentName, PersistableBundle, java.util.concurrent.Executor, OutcomeReceiver)}
+     * if the (calling app package, isolated service package) pair is present in an allow list
+     * that permits data to be returned to the caller.
+     *
+     * @hide
+     */
+    @Nullable private byte[] mOutputData = null;
+
 
 
     // Code below generated by codegen v1.0.23.
@@ -80,12 +91,14 @@
     /* package-private */ ExecuteOutput(
             @Nullable RequestLogRecord requestLogRecord,
             @Nullable RenderingConfig renderingConfig,
-            @NonNull List<EventLogRecord> eventLogRecords) {
+            @NonNull List<EventLogRecord> eventLogRecords,
+            @Nullable byte[] outputData) {
         this.mRequestLogRecord = requestLogRecord;
         this.mRenderingConfig = renderingConfig;
         this.mEventLogRecords = eventLogRecords;
         AnnotationValidations.validate(
                 NonNull.class, null, mEventLogRecords);
+        this.mOutputData = outputData;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -122,6 +135,20 @@
         return mEventLogRecords;
     }
 
+    /**
+     * A byte array returned by an {@link IsolatedService} to a calling app. The contents of
+     * this array is returned to the caller of
+     * {@link OnDevicePersonalizationManager#execute(ComponentName, PersistableBundle, java.util.concurrent.Executor, OutcomeReceiver)}
+     * if the (calling app package, isolated service package) pair is present in an allow list
+     * that permits data to be returned to the caller.
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public @Nullable byte[] getOutputData() {
+        return mOutputData;
+    }
+
     @Override
     @DataClass.Generated.Member
     public boolean equals(@Nullable Object o) {
@@ -137,7 +164,8 @@
         return true
                 && java.util.Objects.equals(mRequestLogRecord, that.mRequestLogRecord)
                 && java.util.Objects.equals(mRenderingConfig, that.mRenderingConfig)
-                && java.util.Objects.equals(mEventLogRecords, that.mEventLogRecords);
+                && java.util.Objects.equals(mEventLogRecords, that.mEventLogRecords)
+                && java.util.Arrays.equals(mOutputData, that.mOutputData);
     }
 
     @Override
@@ -150,6 +178,7 @@
         _hash = 31 * _hash + java.util.Objects.hashCode(mRequestLogRecord);
         _hash = 31 * _hash + java.util.Objects.hashCode(mRenderingConfig);
         _hash = 31 * _hash + java.util.Objects.hashCode(mEventLogRecords);
+        _hash = 31 * _hash + java.util.Arrays.hashCode(mOutputData);
         return _hash;
     }
 
@@ -163,6 +192,7 @@
         private @Nullable RequestLogRecord mRequestLogRecord;
         private @Nullable RenderingConfig mRenderingConfig;
         private @NonNull List<EventLogRecord> mEventLogRecords;
+        private @Nullable byte[] mOutputData;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -218,10 +248,27 @@
             return this;
         }
 
+        /**
+         * A byte array returned by an {@link IsolatedService} to a calling app. The contents of
+         * this array is returned to the caller of
+         * {@link OnDevicePersonalizationManager#execute(ComponentName, PersistableBundle, java.util.concurrent.Executor, OutcomeReceiver)}
+         * if the (calling app package, isolated service package) pair is present in an allow list
+         * that permits data to be returned to the caller.
+         *
+         * @hide
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setOutputData(@NonNull byte... value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mOutputData = value;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull ExecuteOutput build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x8; // Mark builder used
+            mBuilderFieldsSet |= 0x10; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mRequestLogRecord = null;
@@ -232,15 +279,19 @@
             if ((mBuilderFieldsSet & 0x4) == 0) {
                 mEventLogRecords = Collections.emptyList();
             }
+            if ((mBuilderFieldsSet & 0x8) == 0) {
+                mOutputData = null;
+            }
             ExecuteOutput o = new ExecuteOutput(
                     mRequestLogRecord,
                     mRenderingConfig,
-                    mEventLogRecords);
+                    mEventLogRecords,
+                    mOutputData);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x8) != 0) {
+            if ((mBuilderFieldsSet & 0x10) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -248,10 +299,10 @@
     }
 
     @DataClass.Generated(
-            time = 1705959458240L,
+            time = 1706683855882L,
             codegenVersion = "1.0.23",
             sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.java",
-            inputSignatures = "private @android.annotation.Nullable android.adservices.ondevicepersonalization.RequestLogRecord mRequestLogRecord\nprivate @android.annotation.Nullable android.adservices.ondevicepersonalization.RenderingConfig mRenderingConfig\nprivate @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"eventLogRecord\") @android.annotation.NonNull java.util.List<android.adservices.ondevicepersonalization.EventLogRecord> mEventLogRecords\nclass ExecuteOutput extends java.lang.Object implements []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)")
+            inputSignatures = "private @android.annotation.Nullable android.adservices.ondevicepersonalization.RequestLogRecord mRequestLogRecord\nprivate @android.annotation.Nullable android.adservices.ondevicepersonalization.RenderingConfig mRenderingConfig\nprivate @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"eventLogRecord\") @android.annotation.NonNull java.util.List<android.adservices.ondevicepersonalization.EventLogRecord> mEventLogRecords\nprivate @android.annotation.Nullable byte[] mOutputData\nclass ExecuteOutput extends java.lang.Object implements []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/framework/java/android/adservices/ondevicepersonalization/ExecuteOutputParcel.java b/framework/java/android/adservices/ondevicepersonalization/ExecuteOutputParcel.java
index 73c160c..54ba5eb 100644
--- a/framework/java/android/adservices/ondevicepersonalization/ExecuteOutputParcel.java
+++ b/framework/java/android/adservices/ondevicepersonalization/ExecuteOutputParcel.java
@@ -52,14 +52,25 @@
      * If the event does not contain a {@link RequestLogRecord} emitted by this package, the
      * EventLogRecord is not written.
      *
-     * @hide
      */
     @DataClass.PluralOf("eventLogRecord")
     @NonNull private List<EventLogRecord> mEventLogRecords = Collections.emptyList();
 
+    /**
+     * A byte array returned by an {@link IsolatedService} to a calling app. The contents of
+     * this array is returned to the caller of
+     * {@link OnDevicePersonalizationManager#execute(ComponentName, PersistableBundle, java.util.concurrent.Executor, OutcomeReceiver)}
+     * if the (calling app package, isolated service package) pair is present in an allow list
+     * that permits data to be returned to the caller.
+     *
+     * @hide
+     */
+    @Nullable private byte[] mOutputData = null;
+
     /** @hide */
     public ExecuteOutputParcel(@NonNull ExecuteOutput value) {
-        this(value.getRequestLogRecord(), value.getRenderingConfig(), value.getEventLogRecords());
+        this(value.getRequestLogRecord(), value.getRenderingConfig(), value.getEventLogRecords(),
+                value.getOutputData());
     }
 
 
@@ -93,17 +104,25 @@
      *   {@link EventLogRecord#getRequestLogRecord()}.
      *   If the event does not contain a {@link RequestLogRecord} emitted by this package, the
      *   EventLogRecord is not written.
+     * @param outputData
+     *   A byte array returned by an {@link IsolatedService} to a calling app. The contents of
+     *   this array is returned to the caller of
+     *   {@link OnDevicePersonalizationManager#execute(ComponentName, PersistableBundle, java.util.concurrent.Executor, OutcomeReceiver)}
+     *   if the (calling app package, isolated service package) pair is present in an allow list
+     *   that permits data to be returned to the caller.
      */
     @DataClass.Generated.Member
     public ExecuteOutputParcel(
             @Nullable RequestLogRecord requestLogRecord,
             @Nullable RenderingConfig renderingConfig,
-            @NonNull List<EventLogRecord> eventLogRecords) {
+            @NonNull List<EventLogRecord> eventLogRecords,
+            @Nullable byte[] outputData) {
         this.mRequestLogRecord = requestLogRecord;
         this.mRenderingConfig = renderingConfig;
         this.mEventLogRecords = eventLogRecords;
         AnnotationValidations.validate(
                 NonNull.class, null, mEventLogRecords);
+        this.mOutputData = outputData;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -133,14 +152,26 @@
      * {@link EventLogRecord#getRequestLogRecord()}.
      * If the event does not contain a {@link RequestLogRecord} emitted by this package, the
      * EventLogRecord is not written.
-     *
-     * @hide
      */
     @DataClass.Generated.Member
     public @NonNull List<EventLogRecord> getEventLogRecords() {
         return mEventLogRecords;
     }
 
+    /**
+     * A byte array returned by an {@link IsolatedService} to a calling app. The contents of
+     * this array is returned to the caller of
+     * {@link OnDevicePersonalizationManager#execute(ComponentName, PersistableBundle, java.util.concurrent.Executor, OutcomeReceiver)}
+     * if the (calling app package, isolated service package) pair is present in an allow list
+     * that permits data to be returned to the caller.
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public @Nullable byte[] getOutputData() {
+        return mOutputData;
+    }
+
     @Override
     @DataClass.Generated.Member
     public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
@@ -154,6 +185,7 @@
         if (mRequestLogRecord != null) dest.writeTypedObject(mRequestLogRecord, flags);
         if (mRenderingConfig != null) dest.writeTypedObject(mRenderingConfig, flags);
         dest.writeParcelableList(mEventLogRecords, flags);
+        dest.writeByteArray(mOutputData);
     }
 
     @Override
@@ -172,12 +204,14 @@
         RenderingConfig renderingConfig = (flg & 0x2) == 0 ? null : (RenderingConfig) in.readTypedObject(RenderingConfig.CREATOR);
         List<EventLogRecord> eventLogRecords = new java.util.ArrayList<>();
         in.readParcelableList(eventLogRecords, EventLogRecord.class.getClassLoader());
+        byte[] outputData = in.createByteArray();
 
         this.mRequestLogRecord = requestLogRecord;
         this.mRenderingConfig = renderingConfig;
         this.mEventLogRecords = eventLogRecords;
         AnnotationValidations.validate(
                 NonNull.class, null, mEventLogRecords);
+        this.mOutputData = outputData;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -197,10 +231,10 @@
     };
 
     @DataClass.Generated(
-            time = 1704832029278L,
+            time = 1706684633171L,
             codegenVersion = "1.0.23",
             sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/ExecuteOutputParcel.java",
-            inputSignatures = "private @android.annotation.Nullable android.adservices.ondevicepersonalization.RequestLogRecord mRequestLogRecord\nprivate @android.annotation.Nullable android.adservices.ondevicepersonalization.RenderingConfig mRenderingConfig\nprivate @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"eventLogRecord\") @android.annotation.NonNull java.util.List<android.adservices.ondevicepersonalization.EventLogRecord> mEventLogRecords\nclass ExecuteOutputParcel extends java.lang.Object implements [android.os.Parcelable]\n@com.android.ondevicepersonalization.internal.util.DataClass(genAidl=false, genBuilder=false)")
+            inputSignatures = "private @android.annotation.Nullable android.adservices.ondevicepersonalization.RequestLogRecord mRequestLogRecord\nprivate @android.annotation.Nullable android.adservices.ondevicepersonalization.RenderingConfig mRenderingConfig\nprivate @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"eventLogRecord\") @android.annotation.NonNull java.util.List<android.adservices.ondevicepersonalization.EventLogRecord> mEventLogRecords\nprivate @android.annotation.Nullable byte[] mOutputData\nclass ExecuteOutputParcel extends java.lang.Object implements [android.os.Parcelable]\n@com.android.ondevicepersonalization.internal.util.DataClass(genAidl=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/framework/java/android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java b/framework/java/android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java
index 966fcfe..d5935e1 100644
--- a/framework/java/android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java
+++ b/framework/java/android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java
@@ -16,19 +16,24 @@
 
 package android.adservices.ondevicepersonalization;
 
+import static android.adservices.ondevicepersonalization.OnDevicePersonalizationPermissions.REGISTER_MEASUREMENT_EVENT;
+
 import android.adservices.ondevicepersonalization.aidl.IExecuteCallback;
 import android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationManagingService;
+import android.adservices.ondevicepersonalization.aidl.IRegisterWebTriggerCallback;
 import android.adservices.ondevicepersonalization.aidl.IRequestSurfacePackageCallback;
 import android.annotation.CallbackExecutor;
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.OutcomeReceiver;
 import android.os.PersistableBundle;
-import android.os.RemoteException;
 import android.os.SystemClock;
 import android.view.SurfaceControlViewHost;
 
@@ -138,14 +143,16 @@
             IExecuteCallback callbackWrapper = new IExecuteCallback.Stub() {
                 @Override
                 public void onSuccess(
-                        String tokenString) {
+                        Bundle callbackResult) {
                     executor.execute(() -> {
                         try {
-                            SurfacePackageToken token;
-                            if (tokenString == null || tokenString.isBlank()) {
-                                token = null;
-                            } else {
-                                token = new SurfacePackageToken(tokenString);
+                            SurfacePackageToken token = null;
+                            if (callbackResult != null) {
+                                String tokenString = callbackResult.getString(
+                                        Constants.EXTRA_SURFACE_PACKAGE_TOKEN_STRING);
+                                if (tokenString != null && !tokenString.isBlank()) {
+                                    token = new SurfacePackageToken(tokenString);
+                                }
                             }
                             receiver.onResult(token);
                         } catch (Exception e) {
@@ -167,8 +174,10 @@
                     new CallerMetadata.Builder().setStartTimeMillis(startTimeMillis).build(),
                     callbackWrapper);
 
-        } catch (RemoteException e) {
-            receiver.onError(new IllegalStateException(e));
+        } catch (IllegalArgumentException | NullPointerException  e) {
+            throw e;
+        } catch (Exception e) {
+            receiver.onError(e);
         }
     }
 
@@ -241,8 +250,66 @@
                     new CallerMetadata.Builder().setStartTimeMillis(startTimeMillis).build(),
                     callbackWrapper);
 
-        } catch (RemoteException e) {
-            receiver.onError(new IllegalStateException(e));
+        } catch (IllegalArgumentException | NullPointerException e) {
+            throw e;
+        } catch (Exception e) {
+            receiver.onError(e);
+        }
+    }
+
+    /**
+     * Registers a web trigger.
+     *
+     * @param destinationUrl the URL of the web page where the triggering event occurred.
+     * @param registrationUrl the URL which returned the triggering data.
+     * @param triggerHeader the payload returned by the registrationUrl in the Odp trigger header.
+     * @param appPackageName the browser app which showed the page where the triggering event
+     *     occurred.
+     * @param executor the {@link Executor} on which to invoke the callback
+     * @param receiver This either returns {@code null} on success, or an exception on failure.
+     *
+     * @hide
+     */
+    @RequiresPermission(REGISTER_MEASUREMENT_EVENT)
+    public void registerWebTrigger(
+            @NonNull Uri destinationUrl,
+            @NonNull Uri registrationUrl,
+            @NonNull String triggerHeader,
+            @NonNull String appPackageName,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Void, Exception> receiver) {
+        Objects.requireNonNull(destinationUrl);
+        Objects.requireNonNull(registrationUrl);
+        Objects.requireNonNull(triggerHeader);
+        Objects.requireNonNull(appPackageName);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+        long startTimeMillis = SystemClock.elapsedRealtime();
+
+        try {
+            final IOnDevicePersonalizationManagingService service =
+                    mServiceBinder.getService(executor);
+            service.registerWebTrigger(
+                    destinationUrl,
+                    registrationUrl,
+                    triggerHeader,
+                    appPackageName,
+                    new CallerMetadata.Builder().setStartTimeMillis(startTimeMillis).build(),
+                    new IRegisterWebTriggerCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(null));
+                        }
+                        @Override
+                        public void onError(int errorCode) {
+                            executor.execute(() -> receiver.onError(createException(errorCode)));
+                        }
+                    }
+            );
+        } catch (IllegalArgumentException | NullPointerException e) {
+            throw e;
+        } catch (Exception e) {
+            receiver.onError(e);
         }
     }
 
diff --git a/framework/java/android/adservices/ondevicepersonalization/SurfacePackageToken.java b/framework/java/android/adservices/ondevicepersonalization/SurfacePackageToken.java
index bb3876e..d412de9 100644
--- a/framework/java/android/adservices/ondevicepersonalization/SurfacePackageToken.java
+++ b/framework/java/android/adservices/ondevicepersonalization/SurfacePackageToken.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 
 import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.internal.annotations.VisibleForTesting;
 
 /**
  * An opaque reference to content that can be displayed in a {@link android.view.SurfaceView}. This
@@ -34,7 +35,9 @@
         mTokenString = tokenString;
     }
 
-    @NonNull String getTokenString() {
+    /** @hide */
+    @VisibleForTesting
+    @NonNull public String getTokenString() {
         return mTokenString;
     }
 }
diff --git a/framework/java/android/adservices/ondevicepersonalization/TrainingExampleRecord.java b/framework/java/android/adservices/ondevicepersonalization/TrainingExampleRecord.java
index 65268c2..470a0c2 100644
--- a/framework/java/android/adservices/ondevicepersonalization/TrainingExampleRecord.java
+++ b/framework/java/android/adservices/ondevicepersonalization/TrainingExampleRecord.java
@@ -16,18 +16,17 @@
 
 package android.adservices.ondevicepersonalization;
 
-import static android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS;
-
 import android.annotation.FlaggedApi;
 import android.annotation.Nullable;
 import android.os.Parcelable;
 
+import com.android.adservices.ondevicepersonalization.flags.Flags;
 import com.android.ondevicepersonalization.internal.util.DataClass;
 
 /**
  * One record of {@link TrainingExamplesOutput}.
  */
-@FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
 @DataClass(genBuilder = true, genAidl = false)
 public final class TrainingExampleRecord implements Parcelable {
     /**
@@ -35,14 +34,16 @@
      * href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/example.proto">
      * tensorflow.Example</a> proto. The maximum allowed example size is 50KB.
      */
-    @Nullable private byte[] mTrainingExample;
+    @Nullable private byte[] mTrainingExample = null;
 
     /**
      * The resumption token byte arrays corresponding to training examples. The last processed
      * example's corresponding resumption token will be passed to {@link
      * IsolatedWorker#onTrainingExamples} to support resumption.
      */
-    @Nullable private byte[] mResumptionToken;
+    @Nullable private byte[] mResumptionToken = null;
+
+
 
     // Code below generated by codegen v1.0.23.
     //
@@ -134,7 +135,6 @@
     /**
      * A builder for {@link TrainingExampleRecord}
      */
-    @FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
     @SuppressWarnings("WeakerAccess")
     @DataClass.Generated.Member
     public static final class Builder {
@@ -144,7 +144,8 @@
 
         private long mBuilderFieldsSet = 0L;
 
-        public Builder(){}
+        public Builder() {
+        }
 
         /**
          * Training example byte arrays. The format is a binary serialized <a
@@ -177,6 +178,12 @@
             checkNotUsed();
             mBuilderFieldsSet |= 0x4; // Mark builder used
 
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mTrainingExample = null;
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mResumptionToken = null;
+            }
             TrainingExampleRecord o = new TrainingExampleRecord(
                     mTrainingExample,
                     mResumptionToken);
@@ -192,7 +199,7 @@
     }
 
     @DataClass.Generated(
-            time = 1705090577195L,
+            time = 1706729212797L,
             codegenVersion = "1.0.23",
             sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExampleRecord.java",
             inputSignatures = "private @android.annotation.Nullable byte[] mTrainingExample\nprivate @android.annotation.Nullable byte[] mResumptionToken\nclass TrainingExampleRecord extends java.lang.Object implements [android.os.Parcelable]\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genAidl=false)")
diff --git a/framework/java/android/adservices/ondevicepersonalization/aidl/IExecuteCallback.aidl b/framework/java/android/adservices/ondevicepersonalization/aidl/IExecuteCallback.aidl
index ccfc91d..1132c03 100644
--- a/framework/java/android/adservices/ondevicepersonalization/aidl/IExecuteCallback.aidl
+++ b/framework/java/android/adservices/ondevicepersonalization/aidl/IExecuteCallback.aidl
@@ -16,8 +16,10 @@
 
 package android.adservices.ondevicepersonalization.aidl;
 
+import android.os.Bundle;
+
 /** @hide */
 oneway interface IExecuteCallback {
-    void onSuccess(in String result);
+    void onSuccess(in Bundle result);
     void onError(int errorCode);
 }
diff --git a/src/com/android/ondevicepersonalization/services/Flags.java b/src/com/android/ondevicepersonalization/services/Flags.java
index 70d6294..9e7f6c4 100644
--- a/src/com/android/ondevicepersonalization/services/Flags.java
+++ b/src/com/android/ondevicepersonalization/services/Flags.java
@@ -58,6 +58,48 @@
      */
     boolean DEFAULT_SHARED_ISOLATED_PROCESS_FEATURE_ENABLED = false;
 
+    String DEFAULT_CALLER_APP_ALLOW_LIST =
+            "android.ondevicepersonalization,"
+                    + "android.ondevicepersonalization.test.scenario,"
+                    + "com.android.federatedcompute.services,"
+                    + "com.android.libraries.pcc.chronicle.test,"
+                    + "com.android.ondevicepersonalization,"
+                    + "com.android.ondevicepersonalization.cts.e2e,"
+                    + "com.android.ondevicepersonalization.federatedcomputetests,"
+                    + "com.android.ondevicepersonalization.libraries.plugin,"
+                    + "com.android.ondevicepersonalization.manualtests,"
+                    + "com.android.ondevicepersonalization.plugintests,"
+                    + "com.android.ondevicepersonalization.services,"
+                    + "com.android.ondevicepersonalization.servicetests,"
+                    + "com.android.ondevicepersonalization.systemserviceapitests,"
+                    + "com.android.ondevicepersonalization.systemserviceimpltests,"
+                    + "com.android.ondevicepersonalization.testing.sampleservice,"
+                    + "com.example.odpclient,"
+                    + "com.example.odpsamplenetwork,"
+                    + "com.example.odptargetingapp1,"
+                    + "com.example.odptargetingapp2";
+
+    String DEFAULT_ISOLATED_SERVICE_ALLOW_LIST =
+            "android.ondevicepersonalization,"
+                    + "android.ondevicepersonalization.test.scenario,"
+                    + "com.android.federatedcompute.services,"
+                    + "com.android.libraries.pcc.chronicle.test,"
+                    + "com.android.ondevicepersonalization,"
+                    + "com.android.ondevicepersonalization.cts.e2e,"
+                    + "com.android.ondevicepersonalization.federatedcomputetests,"
+                    + "com.android.ondevicepersonalization.libraries.plugin,"
+                    + "com.android.ondevicepersonalization.manualtests,"
+                    + "com.android.ondevicepersonalization.plugintests,"
+                    + "com.android.ondevicepersonalization.services,"
+                    + "com.android.ondevicepersonalization.servicetests,"
+                    + "com.android.ondevicepersonalization.systemserviceapitests,"
+                    + "com.android.ondevicepersonalization.systemserviceimpltests,"
+                    + "com.android.ondevicepersonalization.testing.sampleservice,"
+                    + "com.example.odpclient,"
+                    + "com.example.odpsamplenetwork,"
+                    + "com.example.odptargetingapp1,"
+                    + "com.example.odptargetingapp2";
+
     default boolean getGlobalKillSwitch() {
         return GLOBAL_KILL_SWITCH;
     }
@@ -81,4 +123,12 @@
     default boolean isSharedIsolatedProcessFeatureEnabled() {
         return DEFAULT_SHARED_ISOLATED_PROCESS_FEATURE_ENABLED;
     }
+
+    default String getCallerAppAllowList() {
+        return DEFAULT_CALLER_APP_ALLOW_LIST;
+    }
+
+    default String getIsolatedServiceAllowList() {
+        return DEFAULT_ISOLATED_SERVICE_ALLOW_LIST;
+    }
 }
diff --git a/src/com/android/ondevicepersonalization/services/PhFlags.java b/src/com/android/ondevicepersonalization/services/PhFlags.java
index 33d6c5b..8a975c0 100644
--- a/src/com/android/ondevicepersonalization/services/PhFlags.java
+++ b/src/com/android/ondevicepersonalization/services/PhFlags.java
@@ -42,6 +42,10 @@
     static final String KEY_SHARED_ISOLATED_PROCESS_FEATURE_ENABLED =
             "shared_isolated_process_feature_enabled";
 
+    static final String KEY_CALLER_APP_ALLOW_LIST = "caller_app_allow_list";
+
+    static final String KEY_ISOLATED_SERVICE_ALLOW_LIST = "isolated_service_allow_list";
+
     // OnDevicePersonalization Namespace String from DeviceConfig class
     static final String NAMESPACE_ON_DEVICE_PERSONALIZATION = "on_device_personalization";
     private static final PhFlags sSingleton = new PhFlags();
@@ -110,4 +114,20 @@
                 /* name= */ KEY_SHARED_ISOLATED_PROCESS_FEATURE_ENABLED,
                 /* defaultValue */ DEFAULT_SHARED_ISOLATED_PROCESS_FEATURE_ENABLED);
     }
+
+    @Override
+    public String getCallerAppAllowList() {
+        return DeviceConfig.getString(
+                /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                /* name */ KEY_CALLER_APP_ALLOW_LIST,
+                /* defaultValue */ DEFAULT_CALLER_APP_ALLOW_LIST);
+    }
+
+    @Override
+    public String getIsolatedServiceAllowList() {
+        return DeviceConfig.getString(
+                /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                /* name */ KEY_ISOLATED_SERVICE_ALLOW_LIST,
+                /* defaultValue */ DEFAULT_ISOLATED_SERVICE_ALLOW_LIST);
+    }
 }
diff --git a/src/com/android/ondevicepersonalization/services/request/AppRequestFlow.java b/src/com/android/ondevicepersonalization/services/request/AppRequestFlow.java
index 1b8cd29..e0d48de 100644
--- a/src/com/android/ondevicepersonalization/services/request/AppRequestFlow.java
+++ b/src/com/android/ondevicepersonalization/services/request/AppRequestFlow.java
@@ -49,6 +49,7 @@
 import com.android.ondevicepersonalization.services.util.CryptUtils;
 import com.android.ondevicepersonalization.services.util.LogUtils;
 import com.android.ondevicepersonalization.services.util.MonotonicClock;
+import com.android.ondevicepersonalization.services.util.PrivacyUtils;
 import com.android.ondevicepersonalization.services.util.StatsUtils;
 
 import com.google.common.util.concurrent.FluentFuture;
@@ -106,6 +107,11 @@
             UserPrivacyStatus privacyStatus = UserPrivacyStatus.getInstance();
             return privacyStatus.isPersonalizationStatusEnabled();
         }
+
+        boolean isOutputDataAllowed(
+                String servicePackageName, String appPackageName, Context context) {
+            return PrivacyUtils.isOutputDataAllowed(servicePackageName, appPackageName, context);
+        }
     }
 
     @NonNull
@@ -172,7 +178,8 @@
             ListenableFuture<IsolatedServiceInfo> loadFuture =
                     mInjector.getProcessRunner().loadIsolatedService(
                         TASK_NAME, mService);
-            ListenableFuture<ExecuteOutputParcel> resultFuture = FluentFuture.from(loadFuture)
+            ListenableFuture<ExecuteOutputParcel> executeResultFuture =
+                    FluentFuture.from(loadFuture)
                     .transformAsync(
                             result -> executeAppRequest(result),
                             mInjector.getExecutor()
@@ -185,14 +192,15 @@
                             mInjector.getExecutor()
                     );
 
-            ListenableFuture<Long> queryIdFuture = FluentFuture.from(resultFuture)
+            ListenableFuture<Long> queryIdFuture = FluentFuture.from(executeResultFuture)
                     .transformAsync(input -> logQuery(input), mInjector.getExecutor());
 
-            ListenableFuture<String> resultTokenFuture =
+            ListenableFuture<Bundle> outputResultFuture =
                     FluentFuture.from(
-                            Futures.whenAllSucceed(resultFuture, queryIdFuture)
+                            Futures.whenAllSucceed(executeResultFuture, queryIdFuture)
                                 .callAsync(
-                                        () -> createToken(resultFuture, queryIdFuture),
+                                        () -> createResultBundle(
+                                                executeResultFuture, queryIdFuture),
                                         mInjector.getExecutor()))
                             .withTimeout(
                                 mInjector.getFlags().getIsolatedServiceDeadlineSeconds(),
@@ -201,11 +209,11 @@
                             );
 
             Futures.addCallback(
-                    resultTokenFuture,
-                    new FutureCallback<String>() {
+                    outputResultFuture,
+                    new FutureCallback<Bundle>() {
                         @Override
-                        public void onSuccess(String token) {
-                            sendSuccessResult(token);
+                        public void onSuccess(Bundle bundle) {
+                            sendSuccessResult(bundle);
                         }
 
                         @Override
@@ -216,7 +224,7 @@
                     },
                     mInjector.getExecutor());
 
-            var unused = Futures.whenAllComplete(loadFuture, resultTokenFuture)
+            var unused = Futures.whenAllComplete(loadFuture, outputResultFuture)
                     .callAsync(() -> mInjector.getProcessRunner().unloadIsolatedService(
                             loadFuture.get()),
                     mInjector.getExecutor());
@@ -278,11 +286,11 @@
                 result.getEventLogRecords());
     }
 
-    private ListenableFuture<String> createToken(
+    private ListenableFuture<Bundle> createResultBundle(
             ListenableFuture<ExecuteOutputParcel> resultFuture,
             ListenableFuture<Long> queryIdFuture) {
         try {
-            sLogger.d(TAG + ": createToken() started.");
+            sLogger.d(TAG + ": createResultBundle() started.");
             ExecuteOutputParcel result = Futures.getDone(resultFuture);
             long queryId = Futures.getDone(queryIdFuture);
             RenderingConfig renderingConfig = result.getRenderingConfig();
@@ -296,17 +304,22 @@
                         mService.getPackageName(), queryId);
                 token = CryptUtils.encrypt(wrapper);
             }
-
-            return Futures.immediateFuture(token);
+            Bundle bundle = new Bundle();
+            bundle.putString(Constants.EXTRA_SURFACE_PACKAGE_TOKEN_STRING, token);
+            if (mInjector.isOutputDataAllowed(
+                    mService.getPackageName(), mCallingPackageName, mContext)) {
+                bundle.putByteArray(Constants.EXTRA_OUTPUT_DATA, result.getOutputData());
+            }
+            return Futures.immediateFuture(bundle);
         } catch (Exception e) {
             return Futures.immediateFailedFuture(e);
         }
     }
 
-    private void sendSuccessResult(String resultToken) {
+    private void sendSuccessResult(Bundle result) {
         int responseCode = Constants.STATUS_SUCCESS;
         try {
-            mCallback.onSuccess(resultToken);
+            mCallback.onSuccess(result);
         } catch (RemoteException e) {
             responseCode = Constants.STATUS_INTERNAL_ERROR;
             sLogger.w(TAG + ": Callback error", e);
diff --git a/src/com/android/ondevicepersonalization/services/util/AllowListUtils.java b/src/com/android/ondevicepersonalization/services/util/AllowListUtils.java
new file mode 100644
index 0000000..ae8a077
--- /dev/null
+++ b/src/com/android/ondevicepersonalization/services/util/AllowListUtils.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 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 com.android.ondevicepersonalization.services.util;
+
+import android.annotation.NonNull;
+
+import java.util.Arrays;
+
+/** A utility class to check a single entity against an allow list */
+public class AllowListUtils {
+    private static final String ALLOW_ALL = "*";
+    private static final String SPLITTER = ",";
+
+    /** check if an entity is in the allow list */
+    public static boolean isAllowListed(final String entityName,
+                                        @NonNull final String allowList) {
+        if (ALLOW_ALL.equals(allowList)) {
+            return true;
+        }
+
+        if (entityName == null || entityName.trim().isEmpty()) {
+            return false;
+        }
+
+        return Arrays.stream(allowList.split(SPLITTER))
+                .map(String::trim)
+                .anyMatch(entityInAllowList -> entityInAllowList.equals(entityName));
+    }
+}
diff --git a/src/com/android/ondevicepersonalization/services/util/PrivacyUtils.java b/src/com/android/ondevicepersonalization/services/util/PrivacyUtils.java
new file mode 100644
index 0000000..91dc430
--- /dev/null
+++ b/src/com/android/ondevicepersonalization/services/util/PrivacyUtils.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 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 com.android.ondevicepersonalization.services.util;
+
+import android.annotation.NonNull;
+import android.content.Context;
+
+/** Privacy utils. */
+public class PrivacyUtils {
+    /** Returns true if the service is allowed to return data to the app. */
+    public static boolean isOutputDataAllowed(
+            @NonNull String servicePackageName,
+            @NonNull String appPackageName,
+            @NonNull Context context) {
+        // TODO(b/323106058): Implement.
+        return false;
+    }
+
+    private PrivacyUtils() {}
+}
diff --git a/tests/federatedcomputetests/src/com/android/federatedcompute/services/common/PhFlagsTest.java b/tests/federatedcomputetests/src/com/android/federatedcompute/services/common/PhFlagsTest.java
index 3415879..553506e 100644
--- a/tests/federatedcomputetests/src/com/android/federatedcompute/services/common/PhFlagsTest.java
+++ b/tests/federatedcomputetests/src/com/android/federatedcompute/services/common/PhFlagsTest.java
@@ -17,16 +17,28 @@
 package com.android.federatedcompute.services.common;
 
 import static com.android.federatedcompute.services.common.Flags.AUTHENTICATION_ENABLED;
+import static com.android.federatedcompute.services.common.Flags.DEFAULT_SCHEDULING_PERIOD_SECS;
 import static com.android.federatedcompute.services.common.Flags.ENCRYPTION_ENABLED;
 import static com.android.federatedcompute.services.common.Flags.FEDERATED_COMPUTE_GLOBAL_KILL_SWITCH;
 import static com.android.federatedcompute.services.common.Flags.HTTP_REQUEST_RETRY_LIMIT;
+import static com.android.federatedcompute.services.common.Flags.MAX_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION;
+import static com.android.federatedcompute.services.common.Flags.MAX_SCHEDULING_PERIOD_SECS;
+import static com.android.federatedcompute.services.common.Flags.MIN_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION;
+import static com.android.federatedcompute.services.common.Flags.TRANSIENT_ERROR_RETRY_DELAY_JITTER_PERCENT;
+import static com.android.federatedcompute.services.common.Flags.TRANSIENT_ERROR_RETRY_DELAY_SECS;
 import static com.android.federatedcompute.services.common.Flags.USE_BACKGROUND_ENCRYPTION_KEY_FETCH;
+import static com.android.federatedcompute.services.common.PhFlags.DEFAULT_SCHEDULING_PERIOD_SECS_CONFIG_NAME;
 import static com.android.federatedcompute.services.common.PhFlags.ENABLE_BACKGROUND_ENCRYPTION_KEY_FETCH;
 import static com.android.federatedcompute.services.common.PhFlags.FCP_ENABLE_AUTHENTICATION;
 import static com.android.federatedcompute.services.common.PhFlags.FCP_ENABLE_ENCRYPTION;
 import static com.android.federatedcompute.services.common.PhFlags.FEDERATED_COMPUTATION_ENCRYPTION_KEY_DOWNLOAD_URL;
 import static com.android.federatedcompute.services.common.PhFlags.HTTP_REQUEST_RETRY_LIMIT_CONFIG_NAME;
 import static com.android.federatedcompute.services.common.PhFlags.KEY_FEDERATED_COMPUTE_KILL_SWITCH;
+import static com.android.federatedcompute.services.common.PhFlags.MAX_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION_CONFIG_NAME;
+import static com.android.federatedcompute.services.common.PhFlags.MAX_SCHEDULING_PERIOD_SECS_CONFIG_NAME;
+import static com.android.federatedcompute.services.common.PhFlags.MIN_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION_CONFIG_NAME;
+import static com.android.federatedcompute.services.common.PhFlags.TRANSIENT_ERROR_RETRY_DELAY_JITTER_PERCENT_CONFIG_NAME;
+import static com.android.federatedcompute.services.common.PhFlags.TRANSIENT_ERROR_RETRY_DELAY_SECS_CONFIG_NAME;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -71,6 +83,31 @@
                 FCP_ENABLE_ENCRYPTION,
                 Boolean.toString(ENCRYPTION_ENABLED),
                 /* makeDefault= */ false);
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                MIN_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION_CONFIG_NAME,
+                Long.toString(MIN_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION),
+                /* makeDefault= */ false);
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                MAX_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION_CONFIG_NAME,
+                Long.toString(MAX_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION),
+                /* makeDefault= */ false);
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                DEFAULT_SCHEDULING_PERIOD_SECS_CONFIG_NAME,
+                Long.toString(DEFAULT_SCHEDULING_PERIOD_SECS),
+                /* makeDefault= */ false);
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                MAX_SCHEDULING_PERIOD_SECS_CONFIG_NAME,
+                Long.toString(MAX_SCHEDULING_PERIOD_SECS),
+                /* makeDefault= */ false);
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                TRANSIENT_ERROR_RETRY_DELAY_JITTER_PERCENT_CONFIG_NAME,
+                Float.toString(TRANSIENT_ERROR_RETRY_DELAY_JITTER_PERCENT),
+                /* makeDefault= */ false);
     }
 
     @Test
@@ -201,4 +238,150 @@
         Flags phFlags = FlagsFactory.getFlags();
         assertThat(phFlags.isAuthenticationEnabled()).isEqualTo(overrideEnableAuth);
     }
+
+    @Test
+    public void testGetMinSchedulingIntervalSecsForFederatedComputation() {
+        // Without Overriding
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                MIN_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION_CONFIG_NAME,
+                Long.toString(MIN_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION),
+                /* makeDefault= */ false);
+        assertThat(FlagsFactory.getFlags().getMinSchedulingIntervalSecsForFederatedComputation())
+                .isEqualTo(MIN_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION);
+
+        // Now overriding the value from PH.
+        long overrideMinSchedulingInterval =
+                MIN_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION + 60;
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                MIN_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION_CONFIG_NAME,
+                Long.toString(overrideMinSchedulingInterval),
+                /* makeDefault= */ false);
+
+        Flags phFlags = FlagsFactory.getFlags();
+        assertThat(phFlags.getMinSchedulingIntervalSecsForFederatedComputation())
+                .isEqualTo(overrideMinSchedulingInterval);
+    }
+
+    @Test
+    public void testGetMaxSchedulingIntervalSecsForFederatedComputation() {
+        // Without Overriding
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                MAX_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION_CONFIG_NAME,
+                Long.toString(MAX_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION),
+                /* makeDefault= */ false);
+        assertThat(FlagsFactory.getFlags().getMaxSchedulingIntervalSecsForFederatedComputation())
+                .isEqualTo(MAX_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION);
+
+        // Now overriding the value from PH.
+        long overrideMaxSchedulingInterval =
+                MAX_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION + 60;
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                MAX_SCHEDULING_INTERVAL_SECS_FOR_FEDERATED_COMPUTATION_CONFIG_NAME,
+                Long.toString(overrideMaxSchedulingInterval),
+                /* makeDefault= */ false);
+
+        Flags phFlags = FlagsFactory.getFlags();
+        assertThat(phFlags.getMaxSchedulingIntervalSecsForFederatedComputation())
+                .isEqualTo(overrideMaxSchedulingInterval);
+    }
+
+    @Test
+    public void testGetDefaultSchedulingPeriodSecs() {
+        // Without Overriding
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                DEFAULT_SCHEDULING_PERIOD_SECS_CONFIG_NAME,
+                Long.toString(DEFAULT_SCHEDULING_PERIOD_SECS),
+                /* makeDefault= */ false);
+        assertThat(FlagsFactory.getFlags().getDefaultSchedulingPeriodSecs())
+                .isEqualTo(DEFAULT_SCHEDULING_PERIOD_SECS);
+
+        // Now overriding the value from PH.
+        long overrideDefaultSchedulingPeriodSecs = DEFAULT_SCHEDULING_PERIOD_SECS + 60;
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                DEFAULT_SCHEDULING_PERIOD_SECS_CONFIG_NAME,
+                Long.toString(overrideDefaultSchedulingPeriodSecs),
+                /* makeDefault= */ false);
+
+        Flags phFlags = FlagsFactory.getFlags();
+        assertThat(phFlags.getDefaultSchedulingPeriodSecs())
+                .isEqualTo(overrideDefaultSchedulingPeriodSecs);
+    }
+
+    @Test
+    public void testGetMaxSchedulingPeriodSecs() {
+        // Without Overriding
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                MAX_SCHEDULING_PERIOD_SECS_CONFIG_NAME,
+                Long.toString(MAX_SCHEDULING_PERIOD_SECS),
+                /* makeDefault= */ false);
+        assertThat(FlagsFactory.getFlags().getMaxSchedulingPeriodSecs())
+                .isEqualTo(MAX_SCHEDULING_PERIOD_SECS);
+
+        // Now overriding the value from PH.
+        long overrideMaxSchedulingPeriodSecs = MAX_SCHEDULING_PERIOD_SECS + 60;
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                MAX_SCHEDULING_PERIOD_SECS_CONFIG_NAME,
+                Long.toString(overrideMaxSchedulingPeriodSecs),
+                /* makeDefault= */ false);
+
+        Flags phFlags = FlagsFactory.getFlags();
+        assertThat(phFlags.getMaxSchedulingPeriodSecs()).isEqualTo(overrideMaxSchedulingPeriodSecs);
+    }
+
+    @Test
+    public void testGetTransientErrorRetryDelayJitterPercent() {
+        // Without Overriding
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                TRANSIENT_ERROR_RETRY_DELAY_JITTER_PERCENT_CONFIG_NAME,
+                Float.toString(TRANSIENT_ERROR_RETRY_DELAY_JITTER_PERCENT),
+                /* makeDefault= */ false);
+        assertThat(FlagsFactory.getFlags().getTransientErrorRetryDelayJitterPercent())
+                .isEqualTo(TRANSIENT_ERROR_RETRY_DELAY_JITTER_PERCENT);
+
+        // Now overriding the value from PH.
+        float overrideTransientErrorRetryDelayJitterPercent =
+                TRANSIENT_ERROR_RETRY_DELAY_JITTER_PERCENT + 0.1f;
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                TRANSIENT_ERROR_RETRY_DELAY_JITTER_PERCENT_CONFIG_NAME,
+                Float.toString(overrideTransientErrorRetryDelayJitterPercent),
+                /* makeDefault= */ false);
+
+        Flags phFlags = FlagsFactory.getFlags();
+        assertThat(phFlags.getTransientErrorRetryDelayJitterPercent())
+                .isEqualTo(overrideTransientErrorRetryDelayJitterPercent);
+    }
+
+    @Test
+    public void testGetTransientErrorRetryDelaySecs() {
+        // Without Overriding
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                TRANSIENT_ERROR_RETRY_DELAY_SECS_CONFIG_NAME,
+                Long.toString(TRANSIENT_ERROR_RETRY_DELAY_SECS),
+                /* makeDefault= */ false);
+        assertThat(FlagsFactory.getFlags().getTransientErrorRetryDelaySecs())
+                .isEqualTo(TRANSIENT_ERROR_RETRY_DELAY_SECS);
+
+        // Now overriding the value from PH.
+        long overrideTransientErrorRetryDelaySecs = TRANSIENT_ERROR_RETRY_DELAY_SECS + 15;
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                TRANSIENT_ERROR_RETRY_DELAY_SECS_CONFIG_NAME,
+                Long.toString(overrideTransientErrorRetryDelaySecs),
+                /* makeDefault= */ false);
+
+        Flags phFlags = FlagsFactory.getFlags();
+        assertThat(phFlags.getTransientErrorRetryDelaySecs())
+                .isEqualTo(overrideTransientErrorRetryDelaySecs);
+    }
 }
diff --git a/tests/frameworktests/src/android/adservices/ondevicepersonalization/OnDevicePersonalizationManagerTest.java b/tests/frameworktests/src/android/adservices/ondevicepersonalization/OnDevicePersonalizationManagerTest.java
index bf9db50..a55562c 100644
--- a/tests/frameworktests/src/android/adservices/ondevicepersonalization/OnDevicePersonalizationManagerTest.java
+++ b/tests/frameworktests/src/android/adservices/ondevicepersonalization/OnDevicePersonalizationManagerTest.java
@@ -16,7 +16,10 @@
 
 package android.adservices.ondevicepersonalization;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
 import android.adservices.ondevicepersonalization.aidl.IExecuteCallback;
@@ -26,6 +29,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.OutcomeReceiver;
 import android.os.PersistableBundle;
@@ -47,36 +51,176 @@
 @RunWith(AndroidJUnit4.class)
 public final class OnDevicePersonalizationManagerTest {
     private static final String TAG = "OnDevicePersonalizationManagerTest";
+    private static final String KEY_OP = "op";
     private Context mContext = ApplicationProvider.getApplicationContext();
     private TestServiceBinder mTestBinder = new TestServiceBinder(
             IOnDevicePersonalizationManagingService.Stub.asInterface(new TestService()));
     private OnDevicePersonalizationManager mManager =
             new OnDevicePersonalizationManager(mContext, mTestBinder);
-    private boolean mCallbackSuccess = false;
-    private boolean mCallbackError = false;
-    private CountDownLatch mLatch = new CountDownLatch(1);
 
     @Test
     public void testExecuteSuccess() throws Exception {
+        PersistableBundle params = new PersistableBundle();
+        params.putString(KEY_OP, "ok");
+        var receiver = new ResultReceiver<SurfacePackageToken>();
         mManager.execute(
                 ComponentName.createRelative("com.example.service", ".Example"),
-                PersistableBundle.EMPTY,
+                params,
                 Executors.newSingleThreadExecutor(),
-                new OutcomeReceiver<SurfacePackageToken, Exception>() {
-                    @Override
-                    public void onResult(SurfacePackageToken token) {
-                        mCallbackSuccess = true;
-                        mLatch.countDown();
-                    }
-                    @Override
-                    public void onError(Exception e) {
-                        mCallbackError = true;
-                        mLatch.countDown();
-                    }
-                });
-        mLatch.await();
-        assertTrue(mCallbackSuccess);
-        assertFalse(mCallbackError);
+                receiver);
+        receiver.mLatch.await();
+        assertTrue(receiver.mCallbackSuccess);
+        assertFalse(receiver.mCallbackError);
+        assertNotNull(receiver.mResult);
+        assertEquals(receiver.mResult.getTokenString(), "aaaa");
+    }
+
+    @Test
+    public void testExecuteError() throws Exception {
+        PersistableBundle params = new PersistableBundle();
+        params.putString(KEY_OP, "error");
+        var receiver = new ResultReceiver<SurfacePackageToken>();
+        mManager.execute(
+                ComponentName.createRelative("com.example.service", ".Example"),
+                params,
+                Executors.newSingleThreadExecutor(),
+                receiver);
+        receiver.mLatch.await();
+        assertFalse(receiver.mCallbackSuccess);
+        assertTrue(receiver.mCallbackError);
+    }
+
+    @Test
+    public void testExecutePropagatesIae() throws Exception {
+        PersistableBundle params = new PersistableBundle();
+        params.putString(KEY_OP, "iae");
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> mManager.execute(
+                        ComponentName.createRelative("com.example.service", ".Example"),
+                        params,
+                        Executors.newSingleThreadExecutor(),
+                        new ResultReceiver<SurfacePackageToken>()));
+    }
+
+    @Test
+    public void testExecutePropagatesNpe() throws Exception {
+        PersistableBundle params = new PersistableBundle();
+        params.putString(KEY_OP, "npe");
+        assertThrows(
+                NullPointerException.class,
+                () -> mManager.execute(
+                        ComponentName.createRelative("com.example.service", ".Example"),
+                        params,
+                        Executors.newSingleThreadExecutor(),
+                        new ResultReceiver<SurfacePackageToken>()));
+    }
+
+    @Test
+    public void testExecuteCatchesOtherExceptions() throws Exception {
+        PersistableBundle params = new PersistableBundle();
+        params.putString(KEY_OP, "ise");
+        var receiver = new ResultReceiver<SurfacePackageToken>();
+        mManager.execute(
+                ComponentName.createRelative("com.example.service", ".Example"),
+                params,
+                Executors.newSingleThreadExecutor(),
+                receiver);
+        receiver.mLatch.await();
+        assertFalse(receiver.mCallbackSuccess);
+        assertTrue(receiver.mCallbackError);
+        assertTrue(receiver.mException instanceof IllegalStateException);
+    }
+
+    @Test
+    public void testRegisterWebTriggerSuccess() throws Exception {
+        var receiver = new ResultReceiver<Void>();
+        mManager.registerWebTrigger(
+                Uri.parse("http://example.com"),
+                Uri.parse("http://regurl"),
+                "ok",
+                "com.example.browser",
+                Executors.newSingleThreadExecutor(),
+                receiver);
+        receiver.mLatch.await();
+        assertTrue(receiver.mCallbackSuccess);
+        assertFalse(receiver.mCallbackError);
+    }
+
+    @Test
+    public void testRegisterWebTriggerError() throws Exception {
+        var receiver = new ResultReceiver<Void>();
+        mManager.registerWebTrigger(
+                Uri.parse("http://example.com"),
+                Uri.parse("http://regurl"),
+                "error",
+                "com.example.browser",
+                Executors.newSingleThreadExecutor(),
+                receiver);
+        receiver.mLatch.await();
+        assertFalse(receiver.mCallbackSuccess);
+        assertTrue(receiver.mCallbackError);
+    }
+
+    @Test
+    public void testRegisterWebTriggerPropagatesIae() throws Exception {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> mManager.registerWebTrigger(
+                        Uri.parse("http://example.com"),
+                        Uri.parse("http://regurl"),
+                        "iae",
+                        "com.example.browser",
+                        Executors.newSingleThreadExecutor(),
+                        new ResultReceiver<Void>()));
+    }
+
+    @Test
+    public void testRegisterWebTriggerPropagatesNpe() throws Exception {
+        assertThrows(
+                NullPointerException.class,
+                () -> mManager.registerWebTrigger(
+                        Uri.parse("http://example.com"),
+                        Uri.parse("http://regurl"),
+                        "npe",
+                        "com.example.browser",
+                        Executors.newSingleThreadExecutor(),
+                        new ResultReceiver<Void>()));
+    }
+
+    @Test
+    public void testRegisterWebTriggerCatchesExceptions() throws Exception {
+        var receiver = new ResultReceiver<Void>();
+        mManager.registerWebTrigger(
+                Uri.parse("http://example.com"),
+                Uri.parse("http://regurl"),
+                "ise",
+                "com.example.browser",
+                Executors.newSingleThreadExecutor(),
+                receiver);
+        receiver.mLatch.await();
+        assertFalse(receiver.mCallbackSuccess);
+        assertTrue(receiver.mCallbackError);
+        assertTrue(receiver.mException instanceof IllegalStateException);
+    }
+
+    class ResultReceiver<T> implements OutcomeReceiver<T, Exception> {
+        boolean mCallbackSuccess = false;
+        boolean mCallbackError = false;
+        T mResult = null;
+        Exception mException = null;
+        CountDownLatch mLatch = new CountDownLatch(1);
+        @Override public void onResult(T value) {
+            mCallbackSuccess = true;
+            mResult = value;
+            mLatch.countDown();
+        }
+        @Override
+        public void onError(Exception e) {
+            mCallbackError = true;
+            mException = e;
+            mLatch.countDown();
+        }
     }
 
     class TestService extends IOnDevicePersonalizationManagingService.Stub {
@@ -93,7 +237,22 @@
                 CallerMetadata metadata,
                 IExecuteCallback callback) {
             try {
-                callback.onSuccess("aaaa");
+                String op = params.getString(KEY_OP);
+                if (op.equals("ok")) {
+                    Bundle bundle = new Bundle();
+                    bundle.putString(Constants.EXTRA_SURFACE_PACKAGE_TOKEN_STRING, "aaaa");
+                    callback.onSuccess(bundle);
+                } else if (op.equals("error")) {
+                    callback.onError(Constants.STATUS_INTERNAL_ERROR);
+                } else if (op.equals("iae")) {
+                    throw new IllegalArgumentException();
+                } else if (op.equals("npe")) {
+                    throw new NullPointerException();
+                } else if (op.equals("ise")) {
+                    throw new IllegalStateException();
+                } else {
+                    throw new UnsupportedOperationException();
+                }
             } catch (RemoteException e) {
                 Log.e(TAG, "callback error", e);
             }
@@ -119,7 +278,21 @@
                 String appPackageName,
                 CallerMetadata metadata,
                 IRegisterWebTriggerCallback callback) {
-            throw new UnsupportedOperationException();
+            try {
+                if (triggerHeader.equals("error")) {
+                    callback.onError(Constants.STATUS_INTERNAL_ERROR);
+                } else if (triggerHeader.equals("iae")) {
+                    throw new IllegalArgumentException();
+                } else if (triggerHeader.equals("npe")) {
+                    throw new NullPointerException();
+                } else if (triggerHeader.equals("ise")) {
+                    throw new IllegalStateException();
+                } else {
+                    callback.onSuccess();
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "callback error", e);
+            }
         }
     }
 
diff --git a/tests/perftests/scenarios/src/android/federatedcompute/test/scenario/federatedcompute/TestHelper.java b/tests/perftests/scenarios/src/android/federatedcompute/test/scenario/federatedcompute/TestHelper.java
index bcf7c6a..ce32472 100644
--- a/tests/perftests/scenarios/src/android/federatedcompute/test/scenario/federatedcompute/TestHelper.java
+++ b/tests/perftests/scenarios/src/android/federatedcompute/test/scenario/federatedcompute/TestHelper.java
@@ -107,6 +107,10 @@
         executeShellCommand("am kill com.google.android.ondevicepersonalization.services:"
                 + "com.android.ondevicepersonalization."
                 + "libraries.plugin.internal.PluginExecutorService");
+        executeShellCommand("am kill com.google.android.ondevicepersonalization.services:"
+                + "plugin_disable_art_image_:"
+                + "com.android.ondevicepersonalization."
+                + "libraries.plugin.internal.PluginExecutorService");
         executeShellCommand("am kill com.google.android.federatedcompute");
         executeShellCommand("am kill com.google.android.federatedcompute:"
                 + "com.android.federatedcompute.services.training.IsolatedTrainingService");
diff --git a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/DownloadHelper.java b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/DownloadHelper.java
index 3437c6a..76be68d 100644
--- a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/DownloadHelper.java
+++ b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/DownloadHelper.java
@@ -76,6 +76,10 @@
         executeShellCommand("am kill com.google.android.ondevicepersonalization.services:"
                 + "com.android.ondevicepersonalization."
                 + "libraries.plugin.internal.PluginExecutorService");
+        executeShellCommand("am kill com.google.android.ondevicepersonalization.services:"
+                + "plugin_disable_art_image_:"
+                + "com.android.ondevicepersonalization."
+                + "libraries.plugin.internal.PluginExecutorService");
         SystemClock.sleep(2000);
     }
     public static void pressHome() {
diff --git a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/TestAppHelper.java b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/TestAppHelper.java
index 0b62b3e..3799498 100644
--- a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/TestAppHelper.java
+++ b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/TestAppHelper.java
@@ -94,6 +94,10 @@
         executeShellCommand("am kill com.google.android.ondevicepersonalization.services:"
                 + "com.android.ondevicepersonalization."
                 + "libraries.plugin.internal.PluginExecutorService");
+        executeShellCommand("am kill com.google.android.ondevicepersonalization.services:"
+                + "plugin_disable_art_image_:"
+                + "com.android.ondevicepersonalization."
+                + "libraries.plugin.internal.PluginExecutorService");
         SystemClock.sleep(2000);
     }
 
diff --git a/tests/servicetests/src/com/android/ondevicepersonalization/services/OnDevicePersonalizationManagingServiceTest.java b/tests/servicetests/src/com/android/ondevicepersonalization/services/OnDevicePersonalizationManagingServiceTest.java
index 2124f6c..594a06e 100644
--- a/tests/servicetests/src/com/android/ondevicepersonalization/services/OnDevicePersonalizationManagingServiceTest.java
+++ b/tests/servicetests/src/com/android/ondevicepersonalization/services/OnDevicePersonalizationManagingServiceTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.adservices.ondevicepersonalization.CallerMetadata;
+import android.adservices.ondevicepersonalization.Constants;
 import android.adservices.ondevicepersonalization.aidl.IExecuteCallback;
 import android.adservices.ondevicepersonalization.aidl.IRegisterWebTriggerCallback;
 import android.adservices.ondevicepersonalization.aidl.IRequestSurfacePackageCallback;
@@ -30,6 +31,7 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.PersistableBundle;
 import android.view.SurfaceControlViewHost;
@@ -537,8 +539,10 @@
         private CountDownLatch mLatch = new CountDownLatch(1);
 
         @Override
-        public void onSuccess(String token) {
-            mToken = token;
+        public void onSuccess(Bundle bundle) {
+            if (bundle != null) {
+                mToken = bundle.getString(Constants.EXTRA_SURFACE_PACKAGE_TOKEN_STRING);
+            }
             mLatch.countDown();
         }
 
diff --git a/tests/servicetests/src/com/android/ondevicepersonalization/services/PhFlagsTest.java b/tests/servicetests/src/com/android/ondevicepersonalization/services/PhFlagsTest.java
index f5c94b4..956324b 100644
--- a/tests/servicetests/src/com/android/ondevicepersonalization/services/PhFlagsTest.java
+++ b/tests/servicetests/src/com/android/ondevicepersonalization/services/PhFlagsTest.java
@@ -16,13 +16,17 @@
 
 package com.android.ondevicepersonalization.services;
 
+import static com.android.ondevicepersonalization.services.Flags.DEFAULT_CALLER_APP_ALLOW_LIST;
+import static com.android.ondevicepersonalization.services.Flags.DEFAULT_ISOLATED_SERVICE_ALLOW_LIST;
 import static com.android.ondevicepersonalization.services.Flags.DEFAULT_SHARED_ISOLATED_PROCESS_FEATURE_ENABLED;
 import static com.android.ondevicepersonalization.services.Flags.DEFAULT_TRUSTED_PARTNER_APPS_LIST;
 import static com.android.ondevicepersonalization.services.Flags.ENABLE_PERSONALIZATION_STATUS_OVERRIDE;
 import static com.android.ondevicepersonalization.services.Flags.GLOBAL_KILL_SWITCH;
 import static com.android.ondevicepersonalization.services.Flags.PERSONALIZATION_STATUS_OVERRIDE_VALUE;
+import static com.android.ondevicepersonalization.services.PhFlags.KEY_CALLER_APP_ALLOW_LIST;
 import static com.android.ondevicepersonalization.services.PhFlags.KEY_ENABLE_PERSONALIZATION_STATUS_OVERRIDE;
 import static com.android.ondevicepersonalization.services.PhFlags.KEY_GLOBAL_KILL_SWITCH;
+import static com.android.ondevicepersonalization.services.PhFlags.KEY_ISOLATED_SERVICE_ALLOW_LIST;
 import static com.android.ondevicepersonalization.services.PhFlags.KEY_PERSONALIZATION_STATUS_OVERRIDE_VALUE;
 import static com.android.ondevicepersonalization.services.PhFlags.KEY_SHARED_ISOLATED_PROCESS_FEATURE_ENABLED;
 import static com.android.ondevicepersonalization.services.PhFlags.KEY_TRUSTED_PARTNER_APPS_LIST;
@@ -161,4 +165,52 @@
         assertThat(FlagsFactory.getFlags().isSharedIsolatedProcessFeatureEnabled())
                 .isEqualTo(testIsolatedProcessFeatureEnabled);
     }
+
+    @Test
+    public void testGetCallerAppAllowList() {
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                KEY_CALLER_APP_ALLOW_LIST,
+                DEFAULT_CALLER_APP_ALLOW_LIST,
+                /* makeDefault */ false);
+
+        assertThat(FlagsFactory.getFlags().getCallerAppAllowList())
+                .isEqualTo(DEFAULT_CALLER_APP_ALLOW_LIST);
+
+        final String testCallerAppAllowList =
+                "com.example.odpclient";
+
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                KEY_CALLER_APP_ALLOW_LIST,
+                testCallerAppAllowList,
+                /* makeDefault */ false);
+
+        assertThat(FlagsFactory.getFlags().getCallerAppAllowList())
+                .isEqualTo(testCallerAppAllowList);
+    }
+
+    @Test
+    public void testGetIsolatedServiceAllowList() {
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                KEY_ISOLATED_SERVICE_ALLOW_LIST,
+                DEFAULT_ISOLATED_SERVICE_ALLOW_LIST,
+                /* makeDefault */ false);
+
+        assertThat(FlagsFactory.getFlags().getIsolatedServiceAllowList())
+                .isEqualTo(DEFAULT_ISOLATED_SERVICE_ALLOW_LIST);
+
+        final String testIsolatedServiceAllowList =
+                "com.example.odpsamplenetwork";
+
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+                KEY_ISOLATED_SERVICE_ALLOW_LIST,
+                testIsolatedServiceAllowList,
+                /* makeDefault */ false);
+
+        assertThat(FlagsFactory.getFlags().getIsolatedServiceAllowList())
+                .isEqualTo(testIsolatedServiceAllowList);
+    }
 }
diff --git a/tests/servicetests/src/com/android/ondevicepersonalization/services/download/OnDevicePersonalizationDataProcessingAsyncCallableTests.java b/tests/servicetests/src/com/android/ondevicepersonalization/services/download/OnDevicePersonalizationDataProcessingAsyncCallableTests.java
index 885e1e7..6a4cf5d 100644
--- a/tests/servicetests/src/com/android/ondevicepersonalization/services/download/OnDevicePersonalizationDataProcessingAsyncCallableTests.java
+++ b/tests/servicetests/src/com/android/ondevicepersonalization/services/download/OnDevicePersonalizationDataProcessingAsyncCallableTests.java
@@ -117,7 +117,7 @@
 
         OnDevicePersonalizationDataProcessingAsyncCallable callable =
                 new OnDevicePersonalizationDataProcessingAsyncCallable(mPackageName, mContext);
-        callable.call().get(2000, TimeUnit.MILLISECONDS);
+        callable.call().get(5000, TimeUnit.MILLISECONDS);
         Cursor cursor = dao.readAllVendorData();
         List<VendorData> vendorDataList = new ArrayList<>();
         while (cursor.moveToNext()) {
@@ -168,7 +168,7 @@
 
         OnDevicePersonalizationDataProcessingAsyncCallable callable =
                 new OnDevicePersonalizationDataProcessingAsyncCallable(mPackageName, mContext);
-        callable.call().get(2000, TimeUnit.MILLISECONDS);
+        callable.call().get(5000, TimeUnit.MILLISECONDS);
         Cursor cursor = dao.readAllVendorData();
         List<VendorData> vendorDataList = new ArrayList<>();
         while (cursor.moveToNext()) {
diff --git a/tests/servicetests/src/com/android/ondevicepersonalization/services/request/AppRequestFlowTest.java b/tests/servicetests/src/com/android/ondevicepersonalization/services/request/AppRequestFlowTest.java
index 72e43ce..497782a 100644
--- a/tests/servicetests/src/com/android/ondevicepersonalization/services/request/AppRequestFlowTest.java
+++ b/tests/servicetests/src/com/android/ondevicepersonalization/services/request/AppRequestFlowTest.java
@@ -16,7 +16,9 @@
 
 package com.android.ondevicepersonalization.services.request;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.adservices.ondevicepersonalization.Constants;
@@ -24,6 +26,7 @@
 import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.PersistableBundle;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -64,6 +67,7 @@
     private boolean mCallbackSuccess;
     private boolean mCallbackError;
     private int mCallbackErrorCode;
+    private Bundle mCallbackResult;
 
     @Before
     public void setup() throws Exception {
@@ -93,15 +97,49 @@
     }
 
     @Test
-    public void testRunAppRequestFlow() throws Exception {
+    public void testRunAppRequestFlowOutputDataBlocked() throws Exception {
         AppRequestFlow appRequestFlow = new AppRequestFlow(
                 "abc",
                 new ComponentName(mContext.getPackageName(), "com.test.TestPersonalizationService"),
                 PersistableBundle.EMPTY,
-                new TestCallback(), mContext, 100L, new TestInjector());
+                new TestCallback(), mContext, 100L,
+                new TestInjector() {
+                    @Override public boolean isOutputDataAllowed(
+                        String servicePkg, String appPkg, Context context) {
+                            return false;
+                        }
+                });
         appRequestFlow.run();
         mLatch.await();
         assertTrue(mCallbackSuccess);
+        assertNull(mCallbackResult.getByteArray(Constants.EXTRA_OUTPUT_DATA));
+        assertEquals(2,
+                mDbHelper.getReadableDatabase().query(QueriesContract.QueriesEntry.TABLE_NAME, null,
+                        null, null, null, null, null).getCount());
+        assertEquals(1,
+                mDbHelper.getReadableDatabase().query(EventsContract.EventsEntry.TABLE_NAME, null,
+                        null, null, null, null, null).getCount());
+    }
+
+    @Test
+    public void testRunAppRequestFlowOutputDataAllowed() throws Exception {
+        AppRequestFlow appRequestFlow = new AppRequestFlow(
+                "abc",
+                new ComponentName(mContext.getPackageName(), "com.test.TestPersonalizationService"),
+                PersistableBundle.EMPTY,
+                new TestCallback(), mContext, 100L,
+                new TestInjector() {
+                    @Override public boolean isOutputDataAllowed(
+                        String servicePkg, String appPkg, Context context) {
+                            return true;
+                        }
+                });
+        appRequestFlow.run();
+        mLatch.await();
+        assertTrue(mCallbackSuccess);
+        assertArrayEquals(
+                mCallbackResult.getByteArray(Constants.EXTRA_OUTPUT_DATA),
+                new byte[] {1, 2, 3});
         assertEquals(2,
                 mDbHelper.getReadableDatabase().query(QueriesContract.QueriesEntry.TABLE_NAME, null,
                         null, null, null, null, null).getCount());
@@ -130,8 +168,9 @@
 
     class TestCallback extends IExecuteCallback.Stub {
         @Override
-        public void onSuccess(String token) {
+        public void onSuccess(Bundle bundle) {
             mCallbackSuccess = true;
+            mCallbackResult = bundle;
             mLatch.countDown();
         }
 
diff --git a/tests/servicetests/src/com/android/ondevicepersonalization/services/util/AllowListUtilsTest.java b/tests/servicetests/src/com/android/ondevicepersonalization/services/util/AllowListUtilsTest.java
new file mode 100644
index 0000000..20436a5
--- /dev/null
+++ b/tests/servicetests/src/com/android/ondevicepersonalization/services/util/AllowListUtilsTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 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 com.android.ondevicepersonalization.services.util;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AllowListUtilsTest {
+    @Test
+    public void testIsAllowListed() {
+        assertTrue(AllowListUtils.isAllowListed(null, "*"));
+        assertTrue(AllowListUtils.isAllowListed("", "*"));
+        assertTrue(AllowListUtils.isAllowListed("com.android.app", "*"));
+        assertFalse(AllowListUtils.isAllowListed(null, ""));
+        assertFalse(AllowListUtils.isAllowListed(" ", ""));
+        assertFalse(AllowListUtils.isAllowListed("com.android.app ", ""));
+        assertFalse(AllowListUtils.isAllowListed("com.android.app", "android.app"));
+        assertFalse(AllowListUtils.isAllowListed("com.android.app", "com.play.app"));
+        assertFalse(AllowListUtils.isAllowListed("com.android.app",
+                "com.android.app.extension"));
+        assertFalse(AllowListUtils.isAllowListed("com.android.app ", "com.android.app"));
+        assertTrue(AllowListUtils.isAllowListed("com.android.app", "com.android.app"));
+        assertTrue(AllowListUtils.isAllowListed("com.android.app",
+                "com.play.app,com.android.app"));
+        assertFalse(AllowListUtils.isAllowListed("com.android.app",
+                "com.android.app1,com.android.app2"));
+        assertTrue(AllowListUtils.isAllowListed("com.android.app",
+                " com.android.app , com.play.app "));
+    }
+}
diff --git a/tests/servicetests/src/com/test/TestPersonalizationHandler.java b/tests/servicetests/src/com/test/TestPersonalizationHandler.java
index 5c595aa..203ec30 100644
--- a/tests/servicetests/src/com/test/TestPersonalizationHandler.java
+++ b/tests/servicetests/src/com/test/TestPersonalizationHandler.java
@@ -97,6 +97,7 @@
                                         .setType(1)
                                         .setRowIndex(1)
                                         .build())
+                        .setOutputData(new byte[] {1, 2, 3})
                         .build();
         consumer.accept(result);
     }