Add sources for API 35

Downloaded from https://dl.google.com/android/repository/source-35_r01.zip
using SdkManager in Studio

Test: None
Change-Id: I83f78aa820b66edfdc9f8594d17bc7b6cacccec1
diff --git a/android-35/android/adservices/AdServicesFrameworkInitializer.java b/android-35/android/adservices/AdServicesFrameworkInitializer.java
new file mode 100644
index 0000000..ef61e13
--- /dev/null
+++ b/android-35/android/adservices/AdServicesFrameworkInitializer.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 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 android.adservices;
+
+import static android.adservices.adid.AdIdManager.ADID_SERVICE;
+import static android.adservices.adselection.AdSelectionManager.AD_SELECTION_SERVICE;
+import static android.adservices.appsetid.AppSetIdManager.APPSETID_SERVICE;
+import static android.adservices.common.AdServicesCommonManager.AD_SERVICES_COMMON_SERVICE;
+import static android.adservices.customaudience.CustomAudienceManager.CUSTOM_AUDIENCE_SERVICE;
+import static android.adservices.measurement.MeasurementManager.MEASUREMENT_SERVICE;
+import static android.adservices.signals.ProtectedSignalsManager.PROTECTED_SIGNALS_SERVICE;
+import static android.adservices.topics.TopicsManager.TOPICS_SERVICE;
+
+import android.adservices.adid.AdIdManager;
+import android.adservices.adselection.AdSelectionManager;
+import android.adservices.appsetid.AppSetIdManager;
+import android.adservices.common.AdServicesCommonManager;
+import android.adservices.customaudience.CustomAudienceManager;
+import android.adservices.measurement.MeasurementManager;
+import android.adservices.signals.ProtectedSignalsManager;
+import android.adservices.topics.TopicsManager;
+import android.annotation.SystemApi;
+import android.app.SystemServiceRegistry;
+import android.app.sdksandbox.SdkSandboxSystemServiceRegistry;
+import android.content.Context;
+import android.os.Build;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.adservices.LogUtil;
+
+/**
+ * Class holding initialization code for the AdServices module.
+ *
+ * @hide
+ */
+// TODO(b/269798827): Enable for R.
+@RequiresApi(Build.VERSION_CODES.S)
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public class AdServicesFrameworkInitializer {
+    private AdServicesFrameworkInitializer() {
+    }
+
+    /**
+     * Called by {@link SystemServiceRegistry}'s static initializer and registers all
+     * AdServices services to {@link Context}, so that
+     * {@link Context#getSystemService} can return them.
+     *
+     * @throws IllegalStateException if this is called from anywhere besides
+     *     {@link SystemServiceRegistry}
+     */
+    public static void registerServiceWrappers() {
+        LogUtil.d("Registering AdServices's TopicsManager.");
+        SystemServiceRegistry.registerContextAwareService(
+                TOPICS_SERVICE, TopicsManager.class,
+                (c) -> new TopicsManager(c));
+        // TODO(b/242889021): don't use this workaround on devices that have proper fix
+        SdkSandboxSystemServiceRegistry.getInstance()
+                .registerServiceMutator(
+                        TOPICS_SERVICE,
+                        (service, ctx) -> ((TopicsManager) service).initialize(ctx));
+
+        LogUtil.d("Registering AdServices's CustomAudienceManager.");
+        SystemServiceRegistry.registerContextAwareService(
+                CUSTOM_AUDIENCE_SERVICE, CustomAudienceManager.class, CustomAudienceManager::new);
+        // TODO(b/242889021): don't use this workaround on devices that have proper fix
+        SdkSandboxSystemServiceRegistry.getInstance()
+                .registerServiceMutator(
+                        CUSTOM_AUDIENCE_SERVICE,
+                        (service, ctx) -> ((CustomAudienceManager) service).initialize(ctx));
+
+        LogUtil.d("Registering AdServices's AdSelectionManager.");
+        SystemServiceRegistry.registerContextAwareService(
+                AD_SELECTION_SERVICE, AdSelectionManager.class, AdSelectionManager::new);
+        // TODO(b/242889021): don't use this workaround on devices that have proper fix
+        SdkSandboxSystemServiceRegistry.getInstance()
+                .registerServiceMutator(
+                        AD_SELECTION_SERVICE,
+                        (service, ctx) -> ((AdSelectionManager) service).initialize(ctx));
+
+        LogUtil.d("Registering AdServices's ProtectedSignalsManager.");
+        SystemServiceRegistry.registerContextAwareService(
+                PROTECTED_SIGNALS_SERVICE,
+                ProtectedSignalsManager.class,
+                ProtectedSignalsManager::new);
+        // TODO(b/242889021): don't use this workaround on devices that have proper fix
+        SdkSandboxSystemServiceRegistry.getInstance()
+                .registerServiceMutator(
+                        PROTECTED_SIGNALS_SERVICE,
+                        (service, ctx) -> ((ProtectedSignalsManager) service).initialize(ctx));
+
+        LogUtil.d("Registering AdServices's MeasurementManager.");
+        SystemServiceRegistry.registerContextAwareService(
+                MEASUREMENT_SERVICE, MeasurementManager.class, MeasurementManager::new);
+        // TODO(b/242889021): don't use this workaround on devices that have proper fix
+        SdkSandboxSystemServiceRegistry.getInstance()
+                .registerServiceMutator(
+                        MEASUREMENT_SERVICE,
+                        (service, ctx) -> ((MeasurementManager) service).initialize(ctx));
+
+        LogUtil.d("Registering AdServices's AdIdManager.");
+        SystemServiceRegistry.registerContextAwareService(
+                ADID_SERVICE, AdIdManager.class, (c) -> new AdIdManager(c));
+        // TODO(b/242889021): don't use this workaround on devices that have proper fix
+        SdkSandboxSystemServiceRegistry.getInstance()
+                .registerServiceMutator(
+                        ADID_SERVICE, (service, ctx) -> ((AdIdManager) service).initialize(ctx));
+
+        LogUtil.d("Registering AdServices's AppSetIdManager.");
+        SystemServiceRegistry.registerContextAwareService(
+                APPSETID_SERVICE, AppSetIdManager.class, (c) -> new AppSetIdManager(c));
+        // TODO(b/242889021): don't use this workaround on devices that have proper fix
+        SdkSandboxSystemServiceRegistry.getInstance()
+                .registerServiceMutator(
+                        APPSETID_SERVICE,
+                        (service, ctx) -> ((AppSetIdManager) service).initialize(ctx));
+
+        LogUtil.d("Registering AdServices's AdServicesCommonManager.");
+        SystemServiceRegistry.registerContextAwareService(AD_SERVICES_COMMON_SERVICE,
+                AdServicesCommonManager.class,
+                (c) -> new AdServicesCommonManager(c));
+    }
+}
diff --git a/android-35/android/adservices/AdServicesState.java b/android-35/android/adservices/AdServicesState.java
new file mode 100644
index 0000000..42d2c51
--- /dev/null
+++ b/android-35/android/adservices/AdServicesState.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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 android.adservices;
+
+/** This class specifies the state of the APIs exposed by AdServicesApi apk. */
+public class AdServicesState {
+
+    private AdServicesState() {}
+
+    /**
+     * Returns current state of the {@code AdServicesApi}. The state of AdServicesApi may change
+     * only upon reboot, so this value can be cached, but not persisted, i.e., the value should be
+     * rechecked after a reboot.
+     */
+    public static boolean isAdServicesStateEnabled() {
+        return true;
+    }
+}
+
diff --git a/android-35/android/adservices/AdServicesVersion.java b/android-35/android/adservices/AdServicesVersion.java
new file mode 100644
index 0000000..b059b6e
--- /dev/null
+++ b/android-35/android/adservices/AdServicesVersion.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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 android.adservices;
+
+import android.annotation.SuppressLint;
+
+/**
+ * This class specifies the current version of the AdServices API.
+ *
+ * @removed
+ */
+public class AdServicesVersion {
+
+    /**
+     * @hide
+     */
+    public AdServicesVersion() {}
+
+    /**
+     * The API version of this AdServices API.
+     */
+    @SuppressLint("CompileTimeConstant")
+    public static final int API_VERSION;
+
+    // This variable needs to be initialized in static {} , otherwise javac
+    // would inline these constants and they won't be updatable.
+    static {
+        API_VERSION = 2;
+    }
+}
+
diff --git a/android-35/android/adservices/adid/AdId.java b/android-35/android/adservices/adid/AdId.java
new file mode 100644
index 0000000..5efd2ba
--- /dev/null
+++ b/android-35/android/adservices/adid/AdId.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adid;
+
+import android.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * A unique, user-resettable, device-wide, per-profile ID for advertising.
+ *
+ * <p>Ad networks may use {@code AdId} to monetize for Interest Based Advertising (IBA), i.e.
+ * targeting and remarketing ads. The user may limit availability of this identifier.
+ *
+ * @see AdIdManager#getAdId(Executor, OutcomeReceiver)
+ */
+public class AdId {
+    @NonNull private final String mAdId;
+    private final boolean mLimitAdTrackingEnabled;
+
+    /**
+     * A zeroed-out {@link #getAdId ad id} that is returned when the user has {@link
+     * #isLimitAdTrackingEnabled limited ad tracking}.
+     */
+    public static final String ZERO_OUT = "00000000-0000-0000-0000-000000000000";
+
+    /**
+     * Creates an instance of {@link AdId}
+     *
+     * @param adId obtained from the provider service.
+     * @param limitAdTrackingEnabled value from the provider service which determines the value of
+     *     adId.
+     */
+    public AdId(@NonNull String adId, boolean limitAdTrackingEnabled) {
+        mAdId = adId;
+        mLimitAdTrackingEnabled = limitAdTrackingEnabled;
+    }
+
+    /**
+     * The advertising ID.
+     *
+     * <p>The value of advertising Id depends on a combination of {@link
+     * #isLimitAdTrackingEnabled()} and {@link
+     * android.adservices.common.AdServicesPermissions#ACCESS_ADSERVICES_AD_ID}.
+     *
+     * <p>When the user is {@link #isLimitAdTrackingEnabled limiting ad tracking}, the API returns
+     * {@link #ZERO_OUT}. This disallows a caller to track the user for monetization purposes.
+     *
+     * <p>Otherwise, a string unique to the device and user is returned, which can be used to track
+     * users for advertising.
+     */
+    public @NonNull String getAdId() {
+        return mAdId;
+    }
+
+    /**
+     * Retrieves the limit ad tracking enabled setting.
+     *
+     * <p>This value is true if user has limit ad tracking enabled, {@code false} otherwise.
+     */
+    public boolean isLimitAdTrackingEnabled() {
+        return mLimitAdTrackingEnabled;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof AdId)) {
+            return false;
+        }
+        AdId that = (AdId) o;
+        return mAdId.equals(that.mAdId)
+                && (mLimitAdTrackingEnabled == that.mLimitAdTrackingEnabled);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAdId, mLimitAdTrackingEnabled);
+    }
+
+    @Override
+    public String toString() {
+        return "AdId{"
+                + "mAdId="
+                + mAdId
+                + ", mLimitAdTrackingEnabled='"
+                + mLimitAdTrackingEnabled
+                + '}';
+    }
+}
diff --git a/android-35/android/adservices/adid/AdIdCompatibleManager.java b/android-35/android/adservices/adid/AdIdCompatibleManager.java
new file mode 100644
index 0000000..0d278f3
--- /dev/null
+++ b/android-35/android/adservices/adid/AdIdCompatibleManager.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adid;
+
+import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID;
+
+import android.adservices.common.AdServicesOutcomeReceiver;
+import android.adservices.common.AdServicesStatusUtils;
+import android.adservices.common.CallerMetadata;
+import android.adservices.common.SandboxedSdkContextUtils;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.app.sdksandbox.SandboxedSdkContext;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.SystemClock;
+
+import com.android.adservices.AdServicesCommon;
+import com.android.adservices.LogUtil;
+import com.android.adservices.ServiceBinder;
+import com.android.adservices.shared.common.ServiceUnavailableException;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * AdId Manager provides APIs for app and ad-SDKs to access advertising ID. The advertising ID is a
+ * unique, per-device, user-resettable ID for advertising. It gives users better controls and
+ * provides developers with a simple, standard system to continue to monetize their apps via
+ * personalized ads (formerly known as interest-based ads).
+ *
+ * @hide
+ */
+public class AdIdCompatibleManager {
+    private Context mContext;
+    private ServiceBinder<IAdIdService> mServiceBinder;
+
+    /**
+     * Create AdIdCompatibleManager
+     *
+     * @hide
+     */
+    public AdIdCompatibleManager(@NonNull Context context) {
+        Objects.requireNonNull(context);
+
+        // In case the AdIdManager is initiated from inside a sdk_sandbox process the fields
+        // will be immediately rewritten by the initialize method below.
+        initialize(context);
+    }
+
+    /**
+     * Initializes {@link AdIdCompatibleManager} with the given {@code context}.
+     *
+     * <p>This method is called by the {@link SandboxedSdkContext} to propagate the correct context.
+     * For more information check the javadoc on the {@link
+     * android.app.sdksandbox.SdkSandboxSystemServiceRegistry}.
+     *
+     * @hide
+     * @see android.app.sdksandbox.SdkSandboxSystemServiceRegistry
+     */
+    void initialize(Context context) {
+        mContext = context;
+        mServiceBinder =
+                ServiceBinder.getServiceBinder(
+                        context,
+                        AdServicesCommon.ACTION_ADID_SERVICE,
+                        IAdIdService.Stub::asInterface);
+    }
+
+    @NonNull
+    private IAdIdService getService(
+            @CallbackExecutor Executor executor,
+            AdServicesOutcomeReceiver<AdId, Exception> callback) {
+        IAdIdService service = null;
+        try {
+            service = mServiceBinder.getService();
+
+            // Throw ServiceUnavailableException and set it to the callback.
+            if (service == null) {
+                throw new ServiceUnavailableException();
+            }
+        } catch (RuntimeException e) {
+            LogUtil.e(e, "Failed binding to AdId service");
+            executor.execute(() -> callback.onError(e));
+        }
+
+        return service;
+    }
+
+    @NonNull
+    private Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * Return the AdId. For use on Android R or lower.
+     *
+     * @param executor The executor to run callback.
+     * @param callback The callback that's called after adid are available or an error occurs.
+     * @hide
+     */
+    @RequiresPermission(ACCESS_ADSERVICES_AD_ID)
+    @NonNull
+    public void getAdId(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AdServicesOutcomeReceiver<AdId, Exception> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+        CallerMetadata callerMetadata =
+                new CallerMetadata.Builder()
+                        .setBinderElapsedTimestamp(SystemClock.elapsedRealtime())
+                        .build();
+
+        String appPackageName = "";
+        String sdkPackageName = "";
+        // First check if context is SandboxedSdkContext or not
+        Context getAdIdRequestContext = getContext();
+        SandboxedSdkContext requestContext =
+                SandboxedSdkContextUtils.getAsSandboxedSdkContext(getAdIdRequestContext);
+        if (requestContext != null) {
+            sdkPackageName = requestContext.getSdkPackageName();
+            appPackageName = requestContext.getClientPackageName();
+        } else { // This is the case without the Sandbox.
+            appPackageName = getAdIdRequestContext.getPackageName();
+        }
+
+        try {
+            IAdIdService service = getService(executor, callback);
+            if (service == null) {
+                LogUtil.d("Unable to find AdId service");
+                return;
+            }
+
+            service.getAdId(
+                    new GetAdIdParam.Builder()
+                            .setAppPackageName(appPackageName)
+                            .setSdkPackageName(sdkPackageName)
+                            .build(),
+                    callerMetadata,
+                    new IGetAdIdCallback.Stub() {
+                        @Override
+                        public void onResult(GetAdIdResult resultParcel) {
+                            executor.execute(
+                                    () -> {
+                                        if (resultParcel.isSuccess()) {
+                                            callback.onResult(
+                                                    new AdId(
+                                                            resultParcel.getAdId(),
+                                                            resultParcel.isLatEnabled()));
+                                        } else {
+                                            callback.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            resultParcel));
+                                        }
+                                    });
+                        }
+
+                        @Override
+                        public void onError(int resultCode) {
+                            executor.execute(
+                                    () ->
+                                            callback.onError(
+                                                    AdServicesStatusUtils.asException(resultCode)));
+                        }
+                    });
+        } catch (RemoteException e) {
+            LogUtil.e(e, "RemoteException");
+            callback.onError(e);
+        }
+    }
+
+    /**
+     * If the service is in an APK (as opposed to the system service), unbind it from the service to
+     * allow the APK process to die.
+     *
+     * @hide
+     */
+    // TODO: change to @VisibleForTesting
+    public void unbindFromService() {
+        mServiceBinder.unbindFromService();
+    }
+}
diff --git a/android-35/android/adservices/adid/AdIdManager.java b/android-35/android/adservices/adid/AdIdManager.java
new file mode 100644
index 0000000..4cec751
--- /dev/null
+++ b/android-35/android/adservices/adid/AdIdManager.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adid;
+
+import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID;
+
+import android.adservices.common.AdServicesOutcomeReceiver;
+import android.adservices.common.OutcomeReceiverConverter;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.app.sdksandbox.SandboxedSdkContext;
+import android.content.Context;
+import android.os.Build;
+import android.os.OutcomeReceiver;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.adservices.flags.Flags;
+
+import java.util.concurrent.Executor;
+
+/**
+ * AdId Manager provides APIs for app and ad-SDKs to access advertising ID. The advertising ID is a
+ * unique, per-device, user-resettable ID for advertising. It gives users better controls and
+ * provides developers with a simple, standard system to continue to monetize their apps via
+ * personalized ads (formerly known as interest-based ads).
+ */
+public class AdIdManager {
+    /**
+     * Service used for registering AdIdManager in the system service registry.
+     *
+     * @hide
+     */
+    public static final String ADID_SERVICE = "adid_service";
+
+    // When an app calls the AdId API directly, it sets the SDK name to empty string.
+    static final String EMPTY_SDK = "";
+
+    private final AdIdCompatibleManager mImpl;
+
+    /**
+     * Factory method for creating an instance of AdIdManager.
+     *
+     * @param context The {@link Context} to use
+     * @return A {@link AdIdManager} instance
+     */
+    @NonNull
+    public static AdIdManager get(@NonNull Context context) {
+        // On T+, context.getSystemService() does more than just call constructor.
+        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+                ? context.getSystemService(AdIdManager.class)
+                : new AdIdManager(context);
+    }
+
+    /**
+     * Create AdIdManager
+     *
+     * @hide
+     */
+    public AdIdManager(Context context) {
+        // In case the AdIdManager is initiated from inside a sdk_sandbox process the fields
+        // will be immediately rewritten by the initialize method below.
+        mImpl = new AdIdCompatibleManager(context);
+    }
+
+    /**
+     * Initializes {@link AdIdManager} with the given {@code context}.
+     *
+     * <p>This method is called by the {@link SandboxedSdkContext} to propagate the correct context.
+     * For more information check the javadoc on the {@link
+     * android.app.sdksandbox.SdkSandboxSystemServiceRegistry}.
+     *
+     * @hide
+     * @see android.app.sdksandbox.SdkSandboxSystemServiceRegistry
+     */
+    public AdIdManager initialize(Context context) {
+        mImpl.initialize(context);
+        return this;
+    }
+
+    /**
+     * Return the AdId.
+     *
+     * @param executor The executor to run callback.
+     * @param callback The callback that's called after adid are available or an error occurs.
+     */
+    @RequiresApi(Build.VERSION_CODES.S)
+    @RequiresPermission(ACCESS_ADSERVICES_AD_ID)
+    @NonNull
+    public void getAdId(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<AdId, Exception> callback) {
+        mImpl.getAdId(executor, OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
+    }
+
+    /**
+     * Return the AdId. For use on Android R or lower.
+     *
+     * @param executor The executor to run callback.
+     * @param callback The callback that's called after adid are available or an error occurs.
+     */
+    @FlaggedApi(Flags.FLAG_ADSERVICES_OUTCOMERECEIVER_R_API_ENABLED)
+    @RequiresPermission(ACCESS_ADSERVICES_AD_ID)
+    @NonNull
+    public void getAdId(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AdServicesOutcomeReceiver<AdId, Exception> callback) {
+        mImpl.getAdId(executor, callback);
+    }
+
+    /**
+     * If the service is in an APK (as opposed to the system service), unbind it from the service to
+     * allow the APK process to die.
+     *
+     * @hide
+     */
+    // TODO: change to @VisibleForTesting
+    public void unbindFromService() {
+        mImpl.unbindFromService();
+    }
+}
diff --git a/android-35/android/adservices/adid/AdIdProviderService.java b/android-35/android/adservices/adid/AdIdProviderService.java
new file mode 100644
index 0000000..e9781a8
--- /dev/null
+++ b/android-35/android/adservices/adid/AdIdProviderService.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adid;
+
+import static android.adservices.common.AdServicesStatusUtils.STATUS_SUCCESS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import java.io.IOException;
+
+/**
+ * Abstract Base class for provider service to implement generation of Advertising Id and
+ * limitAdTracking value.
+ *
+ * <p>The implementor of this service needs to override the onGetAdId method and provide a
+ * device-level unique advertising Id and limitAdTracking value on that device.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class AdIdProviderService extends Service {
+
+    /** The intent that the service must respond to. Add it to the intent filter of the service. */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE = "android.adservices.adid.AdIdProviderService";
+
+    /**
+     * Abstract method which will be overridden by provider to provide the adId. For multi-user,
+     * multi-profiles on-device scenarios, separate instance of service per user is expected to
+     * implement this method.
+     */
+    @NonNull
+    public abstract AdId onGetAdId(int clientUid, @NonNull String clientPackageName)
+            throws IOException;
+
+    private final android.adservices.adid.IAdIdProviderService mInterface =
+            new android.adservices.adid.IAdIdProviderService.Stub() {
+                @Override
+                public void getAdIdProvider(
+                        int appUID,
+                        @NonNull String packageName,
+                        @NonNull IGetAdIdProviderCallback resultCallback)
+                        throws RemoteException {
+                    try {
+                        AdId adId = onGetAdId(appUID, packageName);
+                        GetAdIdResult adIdInternal =
+                                new GetAdIdResult.Builder()
+                                        .setStatusCode(STATUS_SUCCESS)
+                                        .setErrorMessage("")
+                                        .setAdId(adId.getAdId())
+                                        .setLatEnabled(adId.isLimitAdTrackingEnabled())
+                                        .build();
+
+                        resultCallback.onResult(adIdInternal);
+                    } catch (Throwable e) {
+                        resultCallback.onError(e.getMessage());
+                    }
+                }
+            };
+
+    @Nullable
+    @Override
+    public final IBinder onBind(@Nullable Intent intent) {
+        return mInterface.asBinder();
+    }
+}
diff --git a/android-35/android/adservices/adid/GetAdIdParam.java b/android-35/android/adservices/adid/GetAdIdParam.java
new file mode 100644
index 0000000..50bf5de
--- /dev/null
+++ b/android-35/android/adservices/adid/GetAdIdParam.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adid;
+
+import static android.adservices.adid.AdIdManager.EMPTY_SDK;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represent input params to the getAdId API.
+ *
+ * @hide
+ */
+public final class GetAdIdParam implements Parcelable {
+    private final String mSdkPackageName;
+    private final String mAppPackageName;
+
+    private GetAdIdParam(@Nullable String sdkPackageName, @NonNull String appPackageName) {
+        mSdkPackageName = sdkPackageName;
+        mAppPackageName = appPackageName;
+    }
+
+    private GetAdIdParam(@NonNull Parcel in) {
+        mSdkPackageName = in.readString();
+        mAppPackageName = in.readString();
+    }
+
+    public static final @NonNull Creator<GetAdIdParam> CREATOR =
+            new Parcelable.Creator<GetAdIdParam>() {
+                @Override
+                public GetAdIdParam createFromParcel(Parcel in) {
+                    return new GetAdIdParam(in);
+                }
+
+                @Override
+                public GetAdIdParam[] newArray(int size) {
+                    return new GetAdIdParam[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeString(mSdkPackageName);
+        out.writeString(mAppPackageName);
+    }
+
+    /** Get the Sdk Package Name. This is the package name in the Manifest. */
+    @NonNull
+    public String getSdkPackageName() {
+        return mSdkPackageName;
+    }
+
+    /** Get the App PackageName. */
+    @NonNull
+    public String getAppPackageName() {
+        return mAppPackageName;
+    }
+
+    /** Builder for {@link GetAdIdParam} objects. */
+    public static final class Builder {
+        private String mSdkPackageName;
+        private String mAppPackageName;
+
+        public Builder() {}
+
+        /**
+         * Set the Sdk Package Name. When the app calls the AdId API directly without using an SDK,
+         * don't set this field.
+         */
+        public @NonNull Builder setSdkPackageName(@NonNull String sdkPackageName) {
+            mSdkPackageName = sdkPackageName;
+            return this;
+        }
+
+        /** Set the App PackageName. */
+        public @NonNull Builder setAppPackageName(@NonNull String appPackageName) {
+            mAppPackageName = appPackageName;
+            return this;
+        }
+
+        /** Builds a {@link GetAdIdParam} instance. */
+        public @NonNull GetAdIdParam build() {
+            if (mSdkPackageName == null) {
+                // When Sdk package name is not set, we assume the App calls the AdId API
+                // directly.
+                // We set the Sdk package name to empty to mark this.
+                mSdkPackageName = EMPTY_SDK;
+            }
+
+            if (mAppPackageName == null || mAppPackageName.isEmpty()) {
+                throw new IllegalArgumentException("App PackageName must not be empty or null");
+            }
+
+            return new GetAdIdParam(mSdkPackageName, mAppPackageName);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adid/GetAdIdResult.java b/android-35/android/adservices/adid/GetAdIdResult.java
new file mode 100644
index 0000000..bfbd191
--- /dev/null
+++ b/android-35/android/adservices/adid/GetAdIdResult.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adid;
+
+import android.adservices.common.AdServicesResponse;
+import android.adservices.common.AdServicesStatusUtils;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Represent the result from the getAdId API.
+ *
+ * @hide
+ */
+public final class GetAdIdResult extends AdServicesResponse {
+    @NonNull private final String mAdId;
+    private final boolean mLimitAdTrackingEnabled;
+
+    private GetAdIdResult(
+            @AdServicesStatusUtils.StatusCode int resultCode,
+            @Nullable String errorMessage,
+            @NonNull String adId,
+            boolean isLimitAdTrackingEnabled) {
+        super(resultCode, errorMessage);
+        mAdId = adId;
+        mLimitAdTrackingEnabled = isLimitAdTrackingEnabled;
+    }
+
+    private GetAdIdResult(@NonNull Parcel in) {
+        super(in);
+        Objects.requireNonNull(in);
+
+        mAdId = in.readString();
+        mLimitAdTrackingEnabled = in.readBoolean();
+    }
+
+    public static final @NonNull Creator<GetAdIdResult> CREATOR =
+            new Parcelable.Creator<GetAdIdResult>() {
+                @Override
+                public GetAdIdResult createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new GetAdIdResult(in);
+                }
+
+                @Override
+                public GetAdIdResult[] newArray(int size) {
+                    return new GetAdIdResult[size];
+                }
+            };
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mStatusCode);
+        out.writeString(mErrorMessage);
+        out.writeString(mAdId);
+        out.writeBoolean(mLimitAdTrackingEnabled);
+    }
+
+    /**
+     * Returns the error message associated with this result.
+     *
+     * <p>If {@link #isSuccess} is {@code true}, the error message is always {@code null}. The error
+     * message may be {@code null} even if {@link #isSuccess} is {@code false}.
+     */
+    @Nullable
+    public String getErrorMessage() {
+        return mErrorMessage;
+    }
+
+    /** Returns the advertising ID associated with this result. */
+    @NonNull
+    public String getAdId() {
+        return mAdId;
+    }
+
+    /** Returns the Limited adtracking field associated with this result. */
+    public boolean isLatEnabled() {
+        return mLimitAdTrackingEnabled;
+    }
+
+    @Override
+    public String toString() {
+        return "GetAdIdResult{"
+                + "mResultCode="
+                + mStatusCode
+                + ", mErrorMessage='"
+                + mErrorMessage
+                + '\''
+                + ", mAdId="
+                + mAdId
+                + ", mLimitAdTrackingEnabled="
+                + mLimitAdTrackingEnabled
+                + '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof GetAdIdResult)) {
+            return false;
+        }
+
+        GetAdIdResult that = (GetAdIdResult) o;
+
+        return mStatusCode == that.mStatusCode
+                && Objects.equals(mErrorMessage, that.mErrorMessage)
+                && Objects.equals(mAdId, that.mAdId)
+                && (mLimitAdTrackingEnabled == that.mLimitAdTrackingEnabled);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mStatusCode, mErrorMessage, mAdId, mLimitAdTrackingEnabled);
+    }
+
+    /**
+     * Builder for {@link GetAdIdResult} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private @AdServicesStatusUtils.StatusCode int mStatusCode;
+        @Nullable private String mErrorMessage;
+        @NonNull private String mAdId;
+        private boolean mLimitAdTrackingEnabled;
+
+        public Builder() {}
+
+        /** Set the Result Code. */
+        public @NonNull Builder setStatusCode(@AdServicesStatusUtils.StatusCode int statusCode) {
+            mStatusCode = statusCode;
+            return this;
+        }
+
+        /** Set the Error Message. */
+        public @NonNull Builder setErrorMessage(@Nullable String errorMessage) {
+            mErrorMessage = errorMessage;
+            return this;
+        }
+
+        /** Set the adid. */
+        public @NonNull Builder setAdId(@NonNull String adId) {
+            mAdId = adId;
+            return this;
+        }
+
+        /** Set the Limited AdTracking enabled field. */
+        public @NonNull Builder setLatEnabled(boolean isLimitAdTrackingEnabled) {
+            mLimitAdTrackingEnabled = isLimitAdTrackingEnabled;
+            return this;
+        }
+
+        /** Builds a {@link GetAdIdResult} instance. */
+        public @NonNull GetAdIdResult build() {
+            if (mAdId == null) {
+                throw new IllegalArgumentException("adId is null");
+            }
+
+            return new GetAdIdResult(mStatusCode, mErrorMessage, mAdId, mLimitAdTrackingEnabled);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/AdSelectionConfig.java b/android-35/android/adservices/adselection/AdSelectionConfig.java
new file mode 100644
index 0000000..3aec741
--- /dev/null
+++ b/android-35/android/adservices/adselection/AdSelectionConfig.java
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import android.adservices.common.AdSelectionSignals;
+import android.adservices.common.AdTechIdentifier;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.AdServicesParcelableUtil;
+import com.android.adservices.flags.Flags;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Contains the configuration of the ad selection process.
+ *
+ * <p>Instances of this class are created by SDKs to be provided as arguments to the {@link
+ * AdSelectionManager#selectAds} and {@link AdSelectionManager#reportImpression} methods in {@link
+ * AdSelectionManager}.
+ */
+// TODO(b/233280314): investigate on adSelectionConfig optimization by merging mCustomAudienceBuyers
+//  and mPerBuyerSignals.
+public final class AdSelectionConfig implements Parcelable {
+    /**
+     * {@link AdSelectionConfig} with empty values for each field.
+     *
+     * @hide
+     */
+    @NonNull public static final AdSelectionConfig EMPTY = new AdSelectionConfig();
+
+    @NonNull private final AdTechIdentifier mSeller;
+    @NonNull private final Uri mDecisionLogicUri;
+    @NonNull private final List<AdTechIdentifier> mCustomAudienceBuyers;
+    @NonNull private final AdSelectionSignals mAdSelectionSignals;
+    @NonNull private final AdSelectionSignals mSellerSignals;
+    @NonNull private final Map<AdTechIdentifier, AdSelectionSignals> mPerBuyerSignals;
+    @NonNull private final Map<AdTechIdentifier, SignedContextualAds> mBuyerSignedContextualAds;
+    @NonNull private final Uri mTrustedScoringSignalsUri;
+
+    @NonNull
+    public static final Creator<AdSelectionConfig> CREATOR =
+            new Creator<AdSelectionConfig>() {
+                @Override
+                public AdSelectionConfig createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new AdSelectionConfig(in);
+                }
+
+                @Override
+                public AdSelectionConfig[] newArray(int size) {
+                    return new AdSelectionConfig[size];
+                }
+            };
+
+    private AdSelectionConfig() {
+        this.mSeller = AdTechIdentifier.fromString("");
+        this.mDecisionLogicUri = Uri.EMPTY;
+        this.mCustomAudienceBuyers = Collections.emptyList();
+        this.mAdSelectionSignals = AdSelectionSignals.EMPTY;
+        this.mSellerSignals = AdSelectionSignals.EMPTY;
+        this.mPerBuyerSignals = Collections.emptyMap();
+        this.mBuyerSignedContextualAds = Collections.emptyMap();
+        this.mTrustedScoringSignalsUri = Uri.EMPTY;
+    }
+
+    private AdSelectionConfig(
+            @NonNull AdTechIdentifier seller,
+            @NonNull Uri decisionLogicUri,
+            @NonNull List<AdTechIdentifier> customAudienceBuyers,
+            @NonNull AdSelectionSignals adSelectionSignals,
+            @NonNull AdSelectionSignals sellerSignals,
+            @NonNull Map<AdTechIdentifier, AdSelectionSignals> perBuyerSignals,
+            @NonNull Map<AdTechIdentifier, SignedContextualAds> perBuyerSignedContextualAds,
+            @NonNull Uri trustedScoringSignalsUri) {
+        this.mSeller = seller;
+        this.mDecisionLogicUri = decisionLogicUri;
+        this.mCustomAudienceBuyers = customAudienceBuyers;
+        this.mAdSelectionSignals = adSelectionSignals;
+        this.mSellerSignals = sellerSignals;
+        this.mPerBuyerSignals = perBuyerSignals;
+        this.mBuyerSignedContextualAds = perBuyerSignedContextualAds;
+        this.mTrustedScoringSignalsUri = trustedScoringSignalsUri;
+    }
+
+    private AdSelectionConfig(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+        mSeller = AdTechIdentifier.CREATOR.createFromParcel(in);
+        mDecisionLogicUri = Uri.CREATOR.createFromParcel(in);
+        mCustomAudienceBuyers = in.createTypedArrayList(AdTechIdentifier.CREATOR);
+        mAdSelectionSignals = AdSelectionSignals.CREATOR.createFromParcel(in);
+        mSellerSignals = AdSelectionSignals.CREATOR.createFromParcel(in);
+        mPerBuyerSignals =
+                AdServicesParcelableUtil.readMapFromParcel(
+                        in, AdTechIdentifier::fromString, AdSelectionSignals.class);
+        mBuyerSignedContextualAds =
+                AdServicesParcelableUtil.readMapFromParcel(
+                        in, AdTechIdentifier::fromString, SignedContextualAds.class);
+        mTrustedScoringSignalsUri = Uri.CREATOR.createFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        mSeller.writeToParcel(dest, flags);
+        mDecisionLogicUri.writeToParcel(dest, flags);
+        dest.writeTypedList(mCustomAudienceBuyers);
+        mAdSelectionSignals.writeToParcel(dest, flags);
+        mSellerSignals.writeToParcel(dest, flags);
+        AdServicesParcelableUtil.writeMapToParcel(dest, mPerBuyerSignals);
+        AdServicesParcelableUtil.writeMapToParcel(dest, mBuyerSignedContextualAds);
+        mTrustedScoringSignalsUri.writeToParcel(dest, flags);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AdSelectionConfig)) return false;
+        AdSelectionConfig that = (AdSelectionConfig) o;
+        return Objects.equals(mSeller, that.mSeller)
+                && Objects.equals(mDecisionLogicUri, that.mDecisionLogicUri)
+                && Objects.equals(mCustomAudienceBuyers, that.mCustomAudienceBuyers)
+                && Objects.equals(mAdSelectionSignals, that.mAdSelectionSignals)
+                && Objects.equals(mSellerSignals, that.mSellerSignals)
+                && Objects.equals(mPerBuyerSignals, that.mPerBuyerSignals)
+                && Objects.equals(mBuyerSignedContextualAds, that.mBuyerSignedContextualAds)
+                && Objects.equals(mTrustedScoringSignalsUri, that.mTrustedScoringSignalsUri);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mSeller,
+                mDecisionLogicUri,
+                mCustomAudienceBuyers,
+                mAdSelectionSignals,
+                mSellerSignals,
+                mPerBuyerSignals,
+                mBuyerSignedContextualAds,
+                mTrustedScoringSignalsUri);
+    }
+
+    /**
+     * @return a new builder instance created from this object's cloned data
+     * @hide
+     */
+    @NonNull
+    public AdSelectionConfig.Builder cloneToBuilder() {
+        return new AdSelectionConfig.Builder()
+                .setSeller(this.getSeller())
+                .setPerBuyerSignedContextualAds(this.getPerBuyerSignedContextualAds())
+                .setAdSelectionSignals(this.getAdSelectionSignals())
+                .setCustomAudienceBuyers(this.getCustomAudienceBuyers())
+                .setDecisionLogicUri(this.getDecisionLogicUri())
+                .setPerBuyerSignals(this.getPerBuyerSignals())
+                .setSellerSignals(this.getSellerSignals())
+                .setTrustedScoringSignalsUri(this.getTrustedScoringSignalsUri());
+    }
+
+    /** @return a AdTechIdentifier of the seller, for example "www.example-ssp.com" */
+    @NonNull
+    public AdTechIdentifier getSeller() {
+        return mSeller;
+    }
+
+    /**
+     * @return the URI used to retrieve the JS code containing the seller/SSP scoreAd function used
+     *     during the ad selection and reporting processes
+     */
+    @NonNull
+    public Uri getDecisionLogicUri() {
+        return mDecisionLogicUri;
+    }
+
+    /**
+     * @return a list of custom audience buyers allowed by the SSP to participate in the ad
+     *     selection process
+     */
+    @NonNull
+    public List<AdTechIdentifier> getCustomAudienceBuyers() {
+        return new ArrayList<>(mCustomAudienceBuyers);
+    }
+
+
+    /**
+     * @return JSON in an AdSelectionSignals object, fetched from the AdSelectionConfig and consumed
+     *     by the JS logic fetched from the DSP, represents signals given to the participating
+     *     buyers in the ad selection and reporting processes.
+     */
+    @NonNull
+    public AdSelectionSignals getAdSelectionSignals() {
+        return mAdSelectionSignals;
+    }
+
+    /**
+     * @return JSON in an AdSelectionSignals object, provided by the SSP and consumed by the JS
+     *     logic fetched from the SSP, represents any information that the SSP used in the ad
+     *     scoring process to tweak the results of the ad selection process (e.g. brand safety
+     *     checks, excluded contextual ads).
+     */
+    @NonNull
+    public AdSelectionSignals getSellerSignals() {
+        return mSellerSignals;
+    }
+
+    /**
+     * @return a Map of buyers and AdSelectionSignals, fetched from the AdSelectionConfig and
+     *     consumed by the JS logic fetched from the DSP, representing any information that each
+     *     buyer would provide during ad selection to participants (such as bid floor, ad selection
+     *     type, etc.)
+     */
+    @NonNull
+    public Map<AdTechIdentifier, AdSelectionSignals> getPerBuyerSignals() {
+        return new HashMap<>(mPerBuyerSignals);
+    }
+
+
+    /**
+     * @return a Map of buyers and corresponding Contextual Ads, these ads are expected to be
+     *     pre-downloaded from the contextual path and injected into Ad Selection.
+     */
+    @FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
+    @NonNull
+    public Map<AdTechIdentifier, SignedContextualAds> getPerBuyerSignedContextualAds() {
+        return new HashMap<>(mBuyerSignedContextualAds);
+    }
+
+    /**
+     * @return URI endpoint of sell-side trusted signal from which creative specific realtime
+     *     information can be fetched from.
+     */
+    @NonNull
+    public Uri getTrustedScoringSignalsUri() {
+        return mTrustedScoringSignalsUri;
+    }
+
+    /** Builder for {@link AdSelectionConfig} object. */
+    public static final class Builder {
+        private AdTechIdentifier mSeller;
+        private Uri mDecisionLogicUri;
+        private List<AdTechIdentifier> mCustomAudienceBuyers;
+        private AdSelectionSignals mAdSelectionSignals = AdSelectionSignals.EMPTY;
+        private AdSelectionSignals mSellerSignals = AdSelectionSignals.EMPTY;
+        private Map<AdTechIdentifier, AdSelectionSignals> mPerBuyerSignals = Collections.emptyMap();
+        private Map<AdTechIdentifier, SignedContextualAds> mBuyerSignedContextualAds =
+                Collections.emptyMap();
+        private Uri mTrustedScoringSignalsUri;
+
+        public Builder() {}
+
+        /**
+         * Sets the seller identifier.
+         *
+         * <p>See {@link #getSeller()} for more details.
+         */
+        @NonNull
+        public AdSelectionConfig.Builder setSeller(@NonNull AdTechIdentifier seller) {
+            Objects.requireNonNull(seller);
+
+            this.mSeller = seller;
+            return this;
+        }
+
+        /**
+         * Sets the URI used to fetch decision logic for use in the ad selection process. Decision
+         * URI could be either of the two schemas:
+         *
+         * <ul>
+         *   <li><b>HTTPS:</b> HTTPS URIs have to be absolute URIs where the host matches the {@code
+         *       seller}
+         *   <li><b>Ad Selection Prebuilt:</b> Ad Selection Service URIs follow {@code
+         *       ad-selection-prebuilt://ad-selection/<name>?<script-generation-parameters>} format.
+         *       FLEDGE generates the appropriate JS script without the need for a network call.
+         *       <p>Available prebuilt scripts:
+         *       <ul>
+         *         <li><b>{@code highest-bid-wins} for {@code scoreAds} and {@code
+         *             reportResult}:</b> This JS picks the ad with the highest bid for scoring. For
+         *             reporting, the given URI is parameterized with {@code render_uri} and {@code
+         *             bid}. Below parameter(s) are required to use this prebuilt:
+         *             <ul>
+         *               <li><b>{@code reportingUrl}:</b> Base reporting uri that will be
+         *                   parameterized later with {@code render_uri} and {@code bid}
+         *             </ul>
+         *             <p>Ex. If your base reporting URL is "https://www.ssp.com" then, {@code
+         *             ad-selection-prebuilt://ad-selection/highest-bid-wins/?reportingUrl=https://www.ssp.com}
+         *       </ul>
+         * </ul>
+         *
+         * <p>See {@link #getDecisionLogicUri()} for more details.
+         */
+        @NonNull
+        public AdSelectionConfig.Builder setDecisionLogicUri(@NonNull Uri decisionLogicUri) {
+            Objects.requireNonNull(decisionLogicUri);
+
+            this.mDecisionLogicUri = decisionLogicUri;
+            return this;
+        }
+
+        /**
+         * Sets the list of allowed buyers.
+         *
+         * <p>See {@link #getCustomAudienceBuyers()} for more details.
+         */
+        @NonNull
+        public AdSelectionConfig.Builder setCustomAudienceBuyers(
+                @NonNull List<AdTechIdentifier> customAudienceBuyers) {
+            Objects.requireNonNull(customAudienceBuyers);
+
+            this.mCustomAudienceBuyers = customAudienceBuyers;
+            return this;
+        }
+
+        /**
+         * Sets the signals provided to buyers during ad selection bid generation.
+         *
+         * <p>If not set, defaults to the empty JSON.
+         *
+         * <p>See {@link #getAdSelectionSignals()} for more details.
+         */
+        @NonNull
+        public AdSelectionConfig.Builder setAdSelectionSignals(
+                @NonNull AdSelectionSignals adSelectionSignals) {
+            Objects.requireNonNull(adSelectionSignals);
+
+            this.mAdSelectionSignals = adSelectionSignals;
+            return this;
+        }
+
+        /**
+         * Set the signals used to modify ad selection results.
+         *
+         * <p>If not set, defaults to the empty JSON.
+         *
+         * <p>See {@link #getSellerSignals()} for more details.
+         */
+        @NonNull
+        public AdSelectionConfig.Builder setSellerSignals(
+                @NonNull AdSelectionSignals sellerSignals) {
+            Objects.requireNonNull(sellerSignals);
+
+            this.mSellerSignals = sellerSignals;
+            return this;
+        }
+
+        /**
+         * Sets the signals provided by each buyer during ad selection.
+         *
+         * <p>If not set, defaults to an empty map.
+         *
+         * <p>See {@link #getPerBuyerSignals()} for more details.
+         */
+        @NonNull
+        public AdSelectionConfig.Builder setPerBuyerSignals(
+                @NonNull Map<AdTechIdentifier, AdSelectionSignals> perBuyerSignals) {
+            Objects.requireNonNull(perBuyerSignals);
+
+            this.mPerBuyerSignals = perBuyerSignals;
+            return this;
+        }
+
+        /**
+         * Sets the contextual Ads corresponding to each buyer during ad selection.
+         *
+         * <p>If not set, defaults to an empty map.
+         *
+         * <p>See {@link #getPerBuyerSignedContextualAds()} for more details.
+         */
+        @FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
+        @NonNull
+        public AdSelectionConfig.Builder setPerBuyerSignedContextualAds(
+                @NonNull Map<AdTechIdentifier, SignedContextualAds> buyerSignedContextualAds) {
+            Objects.requireNonNull(buyerSignedContextualAds);
+
+            this.mBuyerSignedContextualAds = buyerSignedContextualAds;
+            return this;
+        }
+
+        /**
+         * Sets the URI endpoint of sell-side trusted signal from which creative specific realtime
+         * information can be fetched from.
+         *
+         * <p>If {@link Uri#EMPTY} is passed then network call will be skipped and {@link
+         * AdSelectionSignals#EMPTY} will be passed to ad selection.
+         *
+         * <p>See {@link #getTrustedScoringSignalsUri()} for more details.
+         */
+        @NonNull
+        public AdSelectionConfig.Builder setTrustedScoringSignalsUri(
+                @NonNull Uri trustedScoringSignalsUri) {
+            Objects.requireNonNull(trustedScoringSignalsUri);
+
+            this.mTrustedScoringSignalsUri = trustedScoringSignalsUri;
+            return this;
+        }
+
+        /**
+         * Builds an {@link AdSelectionConfig} instance.
+         *
+         * @throws NullPointerException if any required params are null
+         */
+        @NonNull
+        public AdSelectionConfig build() {
+            Objects.requireNonNull(mSeller, "The seller has not been provided");
+            Objects.requireNonNull(
+                mDecisionLogicUri, "The decision logic URI has not been provided");
+            Objects.requireNonNull(
+                mCustomAudienceBuyers, "The custom audience buyers have not been provided");
+            Objects.requireNonNull(
+                mAdSelectionSignals, "The ad selection signals have not been provided");
+            Objects.requireNonNull(mSellerSignals, "The seller signals have not been provided");
+            Objects.requireNonNull(
+                mPerBuyerSignals, "The per buyer signals have not been provided");
+            Objects.requireNonNull(
+                mBuyerSignedContextualAds,
+                "The buyer signed contextual ads have not been provided");
+            Objects.requireNonNull(
+                mTrustedScoringSignalsUri,
+                "The trusted scoring signals URI have not been provided");
+            return new AdSelectionConfig(
+                    mSeller,
+                    mDecisionLogicUri,
+                    mCustomAudienceBuyers,
+                    mAdSelectionSignals,
+                    mSellerSignals,
+                    mPerBuyerSignals,
+                    mBuyerSignedContextualAds,
+                    mTrustedScoringSignalsUri);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/AdSelectionFromOutcomesConfig.java b/android-35/android/adservices/adselection/AdSelectionFromOutcomesConfig.java
new file mode 100644
index 0000000..531b3cf
--- /dev/null
+++ b/android-35/android/adservices/adselection/AdSelectionFromOutcomesConfig.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import android.adservices.common.AdSelectionSignals;
+import android.adservices.common.AdTechIdentifier;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Contains the configuration of the ad selection process that select a winner from a given list of
+ * ad selection ids.
+ *
+ * <p>Instances of this class are created by SDKs to be provided as arguments to the {@link
+ * AdSelectionManager#selectAds} methods in {@link AdSelectionManager}.
+ */
+public final class AdSelectionFromOutcomesConfig implements Parcelable {
+    @NonNull private final AdTechIdentifier mSeller;
+    @NonNull private final List<Long> mAdSelectionIds;
+    @NonNull private final AdSelectionSignals mSelectionSignals;
+    @NonNull private final Uri mSelectionLogicUri;
+
+    @NonNull
+    public static final Creator<AdSelectionFromOutcomesConfig> CREATOR =
+            new Creator<AdSelectionFromOutcomesConfig>() {
+                @Override
+                public AdSelectionFromOutcomesConfig createFromParcel(@NonNull Parcel in) {
+                    return new AdSelectionFromOutcomesConfig(in);
+                }
+
+                @Override
+                public AdSelectionFromOutcomesConfig[] newArray(int size) {
+                    return new AdSelectionFromOutcomesConfig[size];
+                }
+            };
+
+    private AdSelectionFromOutcomesConfig(
+            @NonNull AdTechIdentifier seller,
+            @NonNull List<Long> adSelectionIds,
+            @NonNull AdSelectionSignals selectionSignals,
+            @NonNull Uri selectionLogicUri) {
+        Objects.requireNonNull(seller);
+        Objects.requireNonNull(adSelectionIds);
+        Objects.requireNonNull(selectionSignals);
+        Objects.requireNonNull(selectionLogicUri);
+
+        this.mSeller = seller;
+        this.mAdSelectionIds = adSelectionIds;
+        this.mSelectionSignals = selectionSignals;
+        this.mSelectionLogicUri = selectionLogicUri;
+    }
+
+    private AdSelectionFromOutcomesConfig(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        this.mSeller = AdTechIdentifier.CREATOR.createFromParcel(in);
+        this.mAdSelectionIds =
+                Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
+                        ? in.readArrayList(Long.class.getClassLoader())
+                        : in.readArrayList(Long.class.getClassLoader(), Long.class);
+        this.mSelectionSignals = AdSelectionSignals.CREATOR.createFromParcel(in);
+        this.mSelectionLogicUri = Uri.CREATOR.createFromParcel(in);
+    }
+
+    /** @return a AdTechIdentifier of the seller, for example "www.example-ssp.com" */
+    @NonNull
+    public AdTechIdentifier getSeller() {
+        return mSeller;
+    }
+
+    /**
+     * @return a list of ad selection ids passed by the SSP to participate in the ad selection from
+     *     outcomes process
+     */
+    @NonNull
+    public List<Long> getAdSelectionIds() {
+        return mAdSelectionIds;
+    }
+
+    /**
+     * @return JSON in an {@link AdSelectionSignals} object, fetched from the {@link
+     *     AdSelectionFromOutcomesConfig} and consumed by the JS logic fetched from the DSP {@code
+     *     SelectionLogicUri}.
+     */
+    @NonNull
+    public AdSelectionSignals getSelectionSignals() {
+        return mSelectionSignals;
+    }
+
+    /**
+     * @return the URI used to retrieve the JS code containing the seller/SSP {@code selectOutcome}
+     *     function used during the ad selection
+     */
+    @NonNull
+    public Uri getSelectionLogicUri() {
+        return mSelectionLogicUri;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        mSeller.writeToParcel(dest, flags);
+        dest.writeList(mAdSelectionIds);
+        mSelectionSignals.writeToParcel(dest, flags);
+        mSelectionLogicUri.writeToParcel(dest, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AdSelectionFromOutcomesConfig)) return false;
+        AdSelectionFromOutcomesConfig that = (AdSelectionFromOutcomesConfig) o;
+        return Objects.equals(this.mSeller, that.mSeller)
+                && Objects.equals(this.mAdSelectionIds, that.mAdSelectionIds)
+                && Objects.equals(this.mSelectionSignals, that.mSelectionSignals)
+                && Objects.equals(this.mSelectionLogicUri, that.mSelectionLogicUri);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSeller, mAdSelectionIds, mSelectionSignals, mSelectionLogicUri);
+    }
+
+    /**
+     * Builder for {@link AdSelectionFromOutcomesConfig} objects. All fields require non-null values
+     * to build.
+     */
+    public static final class Builder {
+        @Nullable private AdTechIdentifier mSeller;
+        @Nullable private List<Long> mAdSelectionIds;
+        @Nullable private AdSelectionSignals mSelectionSignals;
+        @Nullable private Uri mSelectionLogicUri;
+
+        public Builder() {}
+
+        /** Sets the seller {@link AdTechIdentifier}. */
+        @NonNull
+        public AdSelectionFromOutcomesConfig.Builder setSeller(@NonNull AdTechIdentifier seller) {
+            Objects.requireNonNull(seller);
+
+            this.mSeller = seller;
+            return this;
+        }
+
+        /** Sets the list of {@code AdSelectionIds} to participate in the selection process. */
+        @NonNull
+        public AdSelectionFromOutcomesConfig.Builder setAdSelectionIds(
+                @NonNull List<Long> adSelectionIds) {
+            Objects.requireNonNull(adSelectionIds);
+
+            this.mAdSelectionIds = adSelectionIds;
+            return this;
+        }
+
+        /**
+         * Sets the {@code SelectionSignals} to be consumed by the JS script downloaded from {@code
+         * SelectionLogicUri}
+         */
+        @NonNull
+        public AdSelectionFromOutcomesConfig.Builder setSelectionSignals(
+                @NonNull AdSelectionSignals selectionSignals) {
+            Objects.requireNonNull(selectionSignals);
+
+            this.mSelectionSignals = selectionSignals;
+            return this;
+        }
+
+        /**
+         * Sets the {@code SelectionLogicUri}. Selection URI could be either of the two schemas:
+         *
+         * <ul>
+         *   <li><b>HTTPS:</b> HTTPS URIs have to be absolute URIs where the host matches the {@code
+         *       seller}
+         *   <li><b>Ad Selection Prebuilt:</b> Ad Selection Service URIs follow {@code
+         *       ad-selection-prebuilt://ad-selection-from-outcomes/<name>?<script-generation-parameters>}
+         *       format. FLEDGE generates the appropriate JS script without the need for a network
+         *       call.
+         *       <p>Available prebuilt scripts:
+         *       <ul>
+         *         <li><b>{@code waterfall-mediation-truncation} for {@code selectOutcome}:</b> This
+         *             JS implements Waterfall mediation truncation logic. Mediation SDK's ad is
+         *             returned if its bid greater than or equal to the bid floor. Below
+         *             parameter(s) are required to use this prebuilt:
+         *             <ul>
+         *               <li><b>{@code bidFloor}:</b> Key of the bid floor value passed in the
+         *                   {@link AdSelectionFromOutcomesConfig#getSelectionSignals()} that will
+         *                   be compared against mediation SDK's winner ad.
+         *             </ul>
+         *             <p>Ex. If your selection signals look like {@code {"bid_floor": 10}} then,
+         *             {@code
+         *             ad-selection-prebuilt://ad-selection-from-outcomes/waterfall-mediation-truncation/?bidFloor=bid_floor}
+         *       </ul>
+         * </ul>
+         *
+         * {@code AdSelectionIds} and {@code SelectionSignals}.
+         */
+        @NonNull
+        public AdSelectionFromOutcomesConfig.Builder setSelectionLogicUri(
+                @NonNull Uri selectionLogicUri) {
+            Objects.requireNonNull(selectionLogicUri);
+
+            this.mSelectionLogicUri = selectionLogicUri;
+            return this;
+        }
+
+        /** Builds a {@link AdSelectionFromOutcomesConfig} instance. */
+        @NonNull
+        public AdSelectionFromOutcomesConfig build() {
+            Objects.requireNonNull(mSeller);
+            Objects.requireNonNull(mAdSelectionIds);
+            Objects.requireNonNull(mSelectionSignals);
+            Objects.requireNonNull(mSelectionLogicUri);
+
+            return new AdSelectionFromOutcomesConfig(
+                    mSeller, mAdSelectionIds, mSelectionSignals, mSelectionLogicUri);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/AdSelectionFromOutcomesInput.java b/android-35/android/adservices/adselection/AdSelectionFromOutcomesInput.java
new file mode 100644
index 0000000..8e72a07
--- /dev/null
+++ b/android-35/android/adservices/adselection/AdSelectionFromOutcomesInput.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Represents input parameters to the {@link
+ * com.android.adservices.service.adselection.AdSelectionServiceImpl#selectAdsFromOutcomes} API.
+ *
+ * @hide
+ */
+public final class AdSelectionFromOutcomesInput implements Parcelable {
+    @NonNull private final AdSelectionFromOutcomesConfig mAdSelectionFromOutcomesConfig;
+    @NonNull private final String mCallerPackageName;
+
+    @NonNull
+    public static final Creator<AdSelectionFromOutcomesInput> CREATOR =
+            new Creator<AdSelectionFromOutcomesInput>() {
+                @Override
+                public AdSelectionFromOutcomesInput createFromParcel(@NonNull Parcel source) {
+                    return new AdSelectionFromOutcomesInput(source);
+                }
+
+                @Override
+                public AdSelectionFromOutcomesInput[] newArray(int size) {
+                    return new AdSelectionFromOutcomesInput[size];
+                }
+            };
+
+    private AdSelectionFromOutcomesInput(
+            @NonNull AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig,
+            @NonNull String callerPackageName) {
+        Objects.requireNonNull(adSelectionFromOutcomesConfig);
+        Objects.requireNonNull(callerPackageName);
+
+        this.mAdSelectionFromOutcomesConfig = adSelectionFromOutcomesConfig;
+        this.mCallerPackageName = callerPackageName;
+    }
+
+    private AdSelectionFromOutcomesInput(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        this.mAdSelectionFromOutcomesConfig =
+                AdSelectionFromOutcomesConfig.CREATOR.createFromParcel(in);
+        this.mCallerPackageName = in.readString();
+    }
+
+    @NonNull
+    public AdSelectionFromOutcomesConfig getAdSelectionFromOutcomesConfig() {
+        return mAdSelectionFromOutcomesConfig;
+    }
+
+    @NonNull
+    public String getCallerPackageName() {
+        return mCallerPackageName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AdSelectionFromOutcomesInput)) return false;
+        AdSelectionFromOutcomesInput that = (AdSelectionFromOutcomesInput) o;
+        return Objects.equals(
+                        this.mAdSelectionFromOutcomesConfig, that.mAdSelectionFromOutcomesConfig)
+                && Objects.equals(this.mCallerPackageName, that.mCallerPackageName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAdSelectionFromOutcomesConfig, mCallerPackageName);
+    }
+
+    /**
+     * Describe the kinds of special objects contained in this Parcelable instance's marshaled
+     * representation. For example, if the object will include a file descriptor in the output of
+     * {@link #writeToParcel(Parcel, int)}, the return value of this method must include the {@link
+     * #CONTENTS_FILE_DESCRIPTOR} bit.
+     *
+     * @return a bitmask indicating the set of special object types marshaled by this Parcelable
+     *     object instance.
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Flatten this object in to a Parcel.
+     *
+     * @param dest The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written. May be 0 or {@link
+     *     #PARCELABLE_WRITE_RETURN_VALUE}.
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        mAdSelectionFromOutcomesConfig.writeToParcel(dest, flags);
+        dest.writeString(mCallerPackageName);
+    }
+
+    /**
+     * Builder for {@link AdSelectionFromOutcomesInput} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        @Nullable private AdSelectionFromOutcomesConfig mAdSelectionFromOutcomesConfig;
+        @Nullable private String mCallerPackageName;
+
+        public Builder() {}
+
+        /** Sets the {@link AdSelectionFromOutcomesConfig}. */
+        @NonNull
+        public AdSelectionFromOutcomesInput.Builder setAdSelectionFromOutcomesConfig(
+                @NonNull AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig) {
+            Objects.requireNonNull(adSelectionFromOutcomesConfig);
+
+            this.mAdSelectionFromOutcomesConfig = adSelectionFromOutcomesConfig;
+            return this;
+        }
+
+        /** Sets the caller's package name. */
+        @NonNull
+        public AdSelectionFromOutcomesInput.Builder setCallerPackageName(
+                @NonNull String callerPackageName) {
+            Objects.requireNonNull(callerPackageName);
+
+            this.mCallerPackageName = callerPackageName;
+            return this;
+        }
+
+        /** Builds a {@link AdSelectionFromOutcomesInput} instance. */
+        @NonNull
+        public AdSelectionFromOutcomesInput build() {
+            Objects.requireNonNull(mAdSelectionFromOutcomesConfig);
+            Objects.requireNonNull(mCallerPackageName);
+
+            return new AdSelectionFromOutcomesInput(
+                    mAdSelectionFromOutcomesConfig, mCallerPackageName);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/AdSelectionInput.java b/android-35/android/adservices/adselection/AdSelectionInput.java
new file mode 100644
index 0000000..b926f58
--- /dev/null
+++ b/android-35/android/adservices/adselection/AdSelectionInput.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Represent input params to the RunAdSelectionInput API.
+ *
+ * @hide
+ */
+public final class AdSelectionInput implements Parcelable {
+    @Nullable private final AdSelectionConfig mAdSelectionConfig;
+    @Nullable private final String mCallerPackageName;
+
+    @NonNull
+    public static final Creator<AdSelectionInput> CREATOR =
+            new Creator<AdSelectionInput>() {
+                public AdSelectionInput createFromParcel(Parcel in) {
+                    return new AdSelectionInput(in);
+                }
+
+                public AdSelectionInput[] newArray(int size) {
+                    return new AdSelectionInput[size];
+                }
+            };
+
+    private AdSelectionInput(
+            @NonNull AdSelectionConfig adSelectionConfig, @NonNull String callerPackageName) {
+        Objects.requireNonNull(adSelectionConfig);
+
+        this.mAdSelectionConfig = adSelectionConfig;
+        this.mCallerPackageName = callerPackageName;
+    }
+
+    private AdSelectionInput(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        this.mAdSelectionConfig = AdSelectionConfig.CREATOR.createFromParcel(in);
+        this.mCallerPackageName = in.readString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        mAdSelectionConfig.writeToParcel(dest, flags);
+        dest.writeString(mCallerPackageName);
+    }
+
+    /**
+     * Returns the adSelectionConfig, one of the inputs to {@link AdSelectionInput} as noted in
+     * {@code AdSelectionService}.
+     */
+    @NonNull
+    public AdSelectionConfig getAdSelectionConfig() {
+        return mAdSelectionConfig;
+    }
+
+    /** @return the caller package name */
+    @NonNull
+    public String getCallerPackageName() {
+        return mCallerPackageName;
+    }
+
+    /**
+     * Builder for {@link AdSelectionInput} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        @Nullable private AdSelectionConfig mAdSelectionConfig;
+        @Nullable private String mCallerPackageName;
+
+        public Builder() {}
+
+        /** Set the AdSelectionConfig. */
+        @NonNull
+        public AdSelectionInput.Builder setAdSelectionConfig(
+                @NonNull AdSelectionConfig adSelectionConfig) {
+            Objects.requireNonNull(adSelectionConfig);
+
+            this.mAdSelectionConfig = adSelectionConfig;
+            return this;
+        }
+
+        /** Sets the caller's package name. */
+        @NonNull
+        public AdSelectionInput.Builder setCallerPackageName(@NonNull String callerPackageName) {
+            Objects.requireNonNull(callerPackageName);
+
+            this.mCallerPackageName = callerPackageName;
+            return this;
+        }
+
+        /** Builds a {@link AdSelectionInput} instance. */
+        @NonNull
+        public AdSelectionInput build() {
+            Objects.requireNonNull(mAdSelectionConfig);
+            Objects.requireNonNull(mCallerPackageName);
+
+            return new AdSelectionInput(mAdSelectionConfig, mCallerPackageName);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/AdSelectionManager.java b/android-35/android/adservices/adselection/AdSelectionManager.java
new file mode 100644
index 0000000..19b0b83
--- /dev/null
+++ b/android-35/android/adservices/adselection/AdSelectionManager.java
@@ -0,0 +1,1027 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE;
+import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_SELECTION;
+import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_PROTECTED_SIGNALS;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import android.adservices.adid.AdId;
+import android.adservices.adid.AdIdCompatibleManager;
+import android.adservices.common.AdServicesStatusUtils;
+import android.adservices.common.AssetFileDescriptorUtil;
+import android.adservices.common.CallerMetadata;
+import android.adservices.common.FledgeErrorResponse;
+import android.adservices.common.SandboxedSdkContextUtils;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.app.sdksandbox.SandboxedSdkContext;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.os.Build;
+import android.os.LimitExceededException;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.TransactionTooLargeException;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.adservices.AdServicesCommon;
+import com.android.adservices.LoggerFactory;
+import com.android.adservices.ServiceBinder;
+import com.android.adservices.flags.Flags;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * AdSelection Manager provides APIs for app and ad-SDKs to run ad selection processes as well as
+ * report impressions.
+ */
+@RequiresApi(Build.VERSION_CODES.S)
+public class AdSelectionManager {
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger();
+
+    /**
+     * Constant that represents the service name for {@link AdSelectionManager} to be used in {@link
+     * android.adservices.AdServicesFrameworkInitializer#registerServiceWrappers}
+     *
+     * @hide
+     */
+    public static final String AD_SELECTION_SERVICE = "ad_selection_service";
+
+    private static final long AD_ID_TIMEOUT_MS = 400;
+    private static final String DEBUG_API_WARNING_MESSAGE =
+            "To enable debug api, include ACCESS_ADSERVICES_AD_ID "
+                    + "permission and enable advertising ID under device settings";
+    private final Executor mAdIdExecutor = Executors.newCachedThreadPool();
+    @NonNull private Context mContext;
+    @NonNull private ServiceBinder<AdSelectionService> mServiceBinder;
+    @NonNull private AdIdCompatibleManager mAdIdManager;
+    @NonNull private ServiceProvider mServiceProvider;
+
+    /**
+     * Factory method for creating an instance of AdSelectionManager.
+     *
+     * @param context The {@link Context} to use
+     * @return A {@link AdSelectionManager} instance
+     */
+    @NonNull
+    public static AdSelectionManager get(@NonNull Context context) {
+        // On T+, context.getSystemService() does more than just call constructor.
+        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+                ? context.getSystemService(AdSelectionManager.class)
+                : new AdSelectionManager(context);
+    }
+
+    /**
+     * Factory method for creating an instance of AdSelectionManager.
+     *
+     * <p>Note: This is for testing only.
+     *
+     * @param context The {@link Context} to use
+     * @param adIdManager The {@link AdIdCompatibleManager} instance to use
+     * @param adSelectionService The {@link AdSelectionService} instance to use
+     * @return A {@link AdSelectionManager} instance
+     * @hide
+     */
+    @VisibleForTesting
+    @NonNull
+    public static AdSelectionManager get(
+            @NonNull Context context,
+            @NonNull AdIdCompatibleManager adIdManager,
+            @NonNull AdSelectionService adSelectionService) {
+        AdSelectionManager adSelectionManager = AdSelectionManager.get(context);
+        adSelectionManager.mAdIdManager = adIdManager;
+        adSelectionManager.mServiceProvider = () -> adSelectionService;
+        return adSelectionManager;
+    }
+
+    /**
+     * Create AdSelectionManager
+     *
+     * @hide
+     */
+    public AdSelectionManager(@NonNull Context context) {
+        Objects.requireNonNull(context);
+
+        // Initialize the default service provider
+        mServiceProvider = this::doGetService;
+
+        // In case the AdSelectionManager is initiated from inside a sdk_sandbox process the
+        // fields will be immediately rewritten by the initialize method below.
+        initialize(context);
+    }
+
+    /**
+     * Initializes {@link AdSelectionManager} with the given {@code context}.
+     *
+     * <p>This method is called by the {@link SandboxedSdkContext} to propagate the correct context.
+     * For more information check the javadoc on the {@link
+     * android.app.sdksandbox.SdkSandboxSystemServiceRegistry}.
+     *
+     * @hide
+     * @see android.app.sdksandbox.SdkSandboxSystemServiceRegistry
+     */
+    public AdSelectionManager initialize(@NonNull Context context) {
+        Objects.requireNonNull(context);
+
+        mContext = context;
+        mServiceBinder =
+                ServiceBinder.getServiceBinder(
+                        context,
+                        AdServicesCommon.ACTION_AD_SELECTION_SERVICE,
+                        AdSelectionService.Stub::asInterface);
+        mAdIdManager = new AdIdCompatibleManager(context);
+        return this;
+    }
+
+    @NonNull
+    public TestAdSelectionManager getTestAdSelectionManager() {
+        return new TestAdSelectionManager(this);
+    }
+
+    /**
+     * Using this interface {@code getService}'s implementation is decoupled from the default {@link
+     * #doGetService()}. This allows us to inject mock instances of {@link AdSelectionService} to
+     * inspect and test the manager-service boundary.
+     */
+    interface ServiceProvider {
+        @NonNull
+        AdSelectionService getService();
+    }
+
+    @NonNull
+    ServiceProvider getServiceProvider() {
+        return mServiceProvider;
+    }
+
+    @NonNull
+    AdSelectionService doGetService() {
+        return mServiceBinder.getService();
+    }
+
+    /**
+     * Collects custom audience data from device. Returns a compressed and encrypted blob to send to
+     * auction servers for ad selection. For more details, please visit <a
+     * href="https://developer.android.com/design-for-safety/privacy-sandbox/protected-audience-bidding-and-auction-services">Bidding
+     * and Auction Services Explainer</a>.
+     *
+     * <p>Custom audience ads must have a {@code ad_render_id} to be eligible for to be collected.
+     *
+     * <p>See {@link AdSelectionManager#persistAdSelectionResult} for how to process the results of
+     * the ad selection run on server-side with the blob generated by this API.
+     *
+     * <p>The output is passed by the receiver, which either returns an {@link
+     * GetAdSelectionDataOutcome} for a successful run, or an {@link Exception} includes the type of
+     * the exception thrown and the corresponding error message.
+     *
+     * <p>If the {@link IllegalArgumentException} is thrown, it is caused by invalid input argument
+     * the API received to run the ad selection.
+     *
+     * <p>If the {@link IllegalStateException} is thrown with error message "Failure of AdSelection
+     * services.", it is caused by an internal failure of the ad selection service.
+     *
+     * <p>If the {@link TimeoutException} is thrown, it is caused when a timeout is encountered
+     * during bidding, scoring, or overall selection process to find winning Ad.
+     *
+     * <p>If the {@link LimitExceededException} is thrown, it is caused when the calling package
+     * exceeds the allowed rate limits and is throttled.
+     *
+     * <p>If the {@link SecurityException} is thrown, it is caused when the caller is not authorized
+     * or permission is not requested.
+     */
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void getAdSelectionData(
+            @NonNull GetAdSelectionDataRequest request,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<GetAdSelectionDataOutcome, Exception> receiver) {
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+        try {
+            final AdSelectionService service = getServiceProvider().getService();
+            service.getAdSelectionData(
+                    new GetAdSelectionDataInput.Builder()
+                            .setSeller(request.getSeller())
+                            .setCallerPackageName(getCallerPackageName())
+                            .setCoordinatorOriginUri(request.getCoordinatorOriginUri())
+                            .build(),
+                    new CallerMetadata.Builder()
+                            .setBinderElapsedTimestamp(SystemClock.elapsedRealtime())
+                            .build(),
+                    new GetAdSelectionDataCallback.Stub() {
+                        @Override
+                        public void onSuccess(GetAdSelectionDataResponse resultParcel) {
+                            executor.execute(
+                                    () -> {
+                                        byte[] adSelectionData;
+                                        try {
+                                            adSelectionData = getAdSelectionData(resultParcel);
+                                        } catch (IOException e) {
+                                            receiver.onError(
+                                                    new IllegalStateException(
+                                                            "Unable to return the AdSelectionData",
+                                                            e));
+                                            return;
+                                        }
+                                        receiver.onResult(
+                                                new GetAdSelectionDataOutcome.Builder()
+                                                        .setAdSelectionId(
+                                                                resultParcel.getAdSelectionId())
+                                                        .setAdSelectionData(adSelectionData)
+                                                        .build());
+                                    });
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () -> {
+                                        receiver.onError(
+                                                AdServicesStatusUtils.asException(failureParcel));
+                                    });
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service.");
+            receiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service.", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Failure of AdSelection service.");
+            receiver.onError(new IllegalStateException("Failure of AdSelection service.", e));
+        }
+    }
+
+    /**
+     * Persists the ad selection results from the server-side. For more details, please visit <a
+     * href="https://developer.android.com/design-for-safety/privacy-sandbox/protected-audience-bidding-and-auction-services">Bidding
+     * and Auction Services Explainer</a>
+     *
+     * <p>See {@link AdSelectionManager#getAdSelectionData} for how to generate an encrypted blob to
+     * run an ad selection on the server side.
+     *
+     * <p>The output is passed by the receiver, which either returns an {@link AdSelectionOutcome}
+     * for a successful run, or an {@link Exception} includes the type of the exception thrown and
+     * the corresponding error message. The {@link AdSelectionOutcome#getAdSelectionId()} is not
+     * guaranteed to be the same as the {@link
+     * PersistAdSelectionResultRequest#getAdSelectionDataId()} or the deprecated {@link
+     * PersistAdSelectionResultRequest#getAdSelectionId()}.
+     *
+     * <p>If the {@link IllegalArgumentException} is thrown, it is caused by invalid input argument
+     * the API received to run the ad selection.
+     *
+     * <p>If the {@link IllegalStateException} is thrown with error message "Failure of AdSelection
+     * services.", it is caused by an internal failure of the ad selection service.
+     *
+     * <p>If the {@link TimeoutException} is thrown, it is caused when a timeout is encountered
+     * during bidding, scoring, or overall selection process to find winning Ad.
+     *
+     * <p>If the {@link LimitExceededException} is thrown, it is caused when the calling package
+     * exceeds the allowed rate limits and is throttled.
+     *
+     * <p>If the {@link SecurityException} is thrown, it is caused when the caller is not authorized
+     * or permission is not requested.
+     */
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void persistAdSelectionResult(
+            @NonNull PersistAdSelectionResultRequest request,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<AdSelectionOutcome, Exception> receiver) {
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+
+        try {
+            final AdSelectionService service = getServiceProvider().getService();
+            service.persistAdSelectionResult(
+                    new PersistAdSelectionResultInput.Builder()
+                            .setSeller(request.getSeller())
+                            .setAdSelectionId(request.getAdSelectionId())
+                            .setAdSelectionResult(request.getAdSelectionResult())
+                            .setCallerPackageName(getCallerPackageName())
+                            .build(),
+                    new CallerMetadata.Builder()
+                            .setBinderElapsedTimestamp(SystemClock.elapsedRealtime())
+                            .build(),
+                    new PersistAdSelectionResultCallback.Stub() {
+                        @Override
+                        public void onSuccess(PersistAdSelectionResultResponse resultParcel) {
+                            executor.execute(
+                                    () ->
+                                            receiver.onResult(
+                                                    new AdSelectionOutcome.Builder()
+                                                            .setAdSelectionId(
+                                                                    resultParcel.getAdSelectionId())
+                                                            .setRenderUri(
+                                                                    resultParcel.getAdRenderUri())
+                                                            .build()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () -> {
+                                        receiver.onError(
+                                                AdServicesStatusUtils.asException(failureParcel));
+                                    });
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service.");
+            receiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service.", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Failure of AdSelection service.");
+            receiver.onError(new IllegalStateException("Failure of AdSelection service.", e));
+        }
+    }
+
+    /**
+     * Runs the ad selection process on device to select a remarketing ad for the caller
+     * application.
+     *
+     * <p>The input {@code adSelectionConfig} is provided by the Ads SDK and the {@link
+     * AdSelectionConfig} object is transferred via a Binder call. For this reason, the total size
+     * of these objects is bound to the Android IPC limitations. Failures to transfer the {@link
+     * AdSelectionConfig} will throws an {@link TransactionTooLargeException}.
+     *
+     * <p>The input {@code adSelectionConfig} contains {@code Decision Logic Uri} that could follow
+     * either the HTTPS or Ad Selection Prebuilt schemas.
+     *
+     * <p>If the URI follows HTTPS schema then the host should match the {@code seller}. Otherwise,
+     * {@link IllegalArgumentException} will be thrown.
+     *
+     * <p>Prebuilt URIs are a way of substituting a generic pre-built logics for the required
+     * JavaScripts for {@code scoreAds}. Prebuilt Uri for this endpoint should follow;
+     *
+     * <ul>
+     *   <li>{@code ad-selection-prebuilt://ad-selection/<name>?<script-generation-parameters>}
+     * </ul>
+     *
+     * <p>If an unsupported prebuilt URI is passed or prebuilt URI feature is disabled by the
+     * service then {@link IllegalArgumentException} will be thrown.
+     *
+     * <p>See {@link AdSelectionConfig.Builder#setDecisionLogicUri} for supported {@code <name>} and
+     * required {@code <script-generation-parameters>}.
+     *
+     * <p>The output is passed by the receiver, which either returns an {@link AdSelectionOutcome}
+     * for a successful run, or an {@link Exception} includes the type of the exception thrown and
+     * the corresponding error message.
+     *
+     * <p>If the {@link IllegalArgumentException} is thrown, it is caused by invalid input argument
+     * the API received to run the ad selection.
+     *
+     * <p>If the {@link IllegalStateException} is thrown with error message "Failure of AdSelection
+     * services.", it is caused by an internal failure of the ad selection service.
+     *
+     * <p>If the {@link TimeoutException} is thrown, it is caused when a timeout is encountered
+     * during bidding, scoring, or overall selection process to find winning Ad.
+     *
+     * <p>If the {@link LimitExceededException} is thrown, it is caused when the calling package
+     * exceeds the allowed rate limits and is throttled.
+     *
+     * <p>If the {@link SecurityException} is thrown, it is caused when the caller is not authorized
+     * or permission is not requested.
+     */
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void selectAds(
+            @NonNull AdSelectionConfig adSelectionConfig,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<AdSelectionOutcome, Exception> receiver) {
+        Objects.requireNonNull(adSelectionConfig);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+
+        try {
+            final AdSelectionService service = getServiceProvider().getService();
+            service.selectAds(
+                    new AdSelectionInput.Builder()
+                            .setAdSelectionConfig(adSelectionConfig)
+                            .setCallerPackageName(getCallerPackageName())
+                            .build(),
+                    new CallerMetadata.Builder()
+                            .setBinderElapsedTimestamp(SystemClock.elapsedRealtime())
+                            .build(),
+                    new AdSelectionCallback.Stub() {
+                        @Override
+                        public void onSuccess(AdSelectionResponse resultParcel) {
+                            executor.execute(
+                                    () ->
+                                            receiver.onResult(
+                                                    new AdSelectionOutcome.Builder()
+                                                            .setAdSelectionId(
+                                                                    resultParcel.getAdSelectionId())
+                                                            .setRenderUri(
+                                                                    resultParcel.getRenderUri())
+                                                            .build()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () -> {
+                                        receiver.onError(
+                                                AdServicesStatusUtils.asException(failureParcel));
+                                    });
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service.");
+            receiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service.", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Failure of AdSelection service.");
+            receiver.onError(new IllegalStateException("Failure of AdSelection service.", e));
+        }
+    }
+
+    /**
+     * Selects an ad from the results of previously ran ad selections.
+     *
+     * <p>The input {@code adSelectionFromOutcomesConfig} is provided by the Ads SDK and the {@link
+     * AdSelectionFromOutcomesConfig} object is transferred via a Binder call. For this reason, the
+     * total size of these objects is bound to the Android IPC limitations. Failures to transfer the
+     * {@link AdSelectionFromOutcomesConfig} will throws an {@link TransactionTooLargeException}.
+     *
+     * <p>The output is passed by the receiver, which either returns an {@link AdSelectionOutcome}
+     * for a successful run, or an {@link Exception} includes the type of the exception thrown and
+     * the corresponding error message.
+     *
+     * <p>The input {@code adSelectionFromOutcomesConfig} contains:
+     *
+     * <ul>
+     *   <li>{@code Seller} is required to be a registered {@link
+     *       android.adservices.common.AdTechIdentifier}. Otherwise, {@link IllegalStateException}
+     *       will be thrown.
+     *   <li>{@code List of ad selection ids} should exist and come from {@link
+     *       AdSelectionManager#selectAds} calls originated from the same application. Otherwise,
+     *       {@link IllegalArgumentException} for input validation will raise listing violating ad
+     *       selection ids.
+     *   <li>{@code Selection logic URI} that could follow either the HTTPS or Ad Selection Prebuilt
+     *       schemas.
+     *       <p>If the URI follows HTTPS schema then the host should match the {@code seller}.
+     *       Otherwise, {@link IllegalArgumentException} will be thrown.
+     *       <p>Prebuilt URIs are a way of substituting a generic pre-built logics for the required
+     *       JavaScripts for {@code selectOutcome}. Prebuilt Uri for this endpoint should follow;
+     *       <ul>
+     *         <li>{@code
+     *             ad-selection-prebuilt://ad-selection-from-outcomes/<name>?<script-generation-parameters>}
+     *       </ul>
+     *       <p>If an unsupported prebuilt URI is passed or prebuilt URI feature is disabled by the
+     *       service then {@link IllegalArgumentException} will be thrown.
+     *       <p>See {@link AdSelectionFromOutcomesConfig.Builder#setSelectionLogicUri} for supported
+     *       {@code <name>} and required {@code <script-generation-parameters>}.
+     * </ul>
+     *
+     * <p>If the {@link IllegalArgumentException} is thrown, it is caused by invalid input argument
+     * the API received to run the ad selection.
+     *
+     * <p>If the {@link IllegalStateException} is thrown with error message "Failure of AdSelection
+     * services.", it is caused by an internal failure of the ad selection service.
+     *
+     * <p>If the {@link TimeoutException} is thrown, it is caused when a timeout is encountered
+     * during bidding, scoring, or overall selection process to find winning Ad.
+     *
+     * <p>If the {@link LimitExceededException} is thrown, it is caused when the calling package
+     * exceeds the allowed rate limits and is throttled.
+     *
+     * <p>If the {@link SecurityException} is thrown, it is caused when the caller is not authorized
+     * or permission is not requested.
+     */
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void selectAds(
+            @NonNull AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<AdSelectionOutcome, Exception> receiver) {
+        Objects.requireNonNull(adSelectionFromOutcomesConfig);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+
+        try {
+            final AdSelectionService service = getServiceProvider().getService();
+            service.selectAdsFromOutcomes(
+                    new AdSelectionFromOutcomesInput.Builder()
+                            .setAdSelectionFromOutcomesConfig(adSelectionFromOutcomesConfig)
+                            .setCallerPackageName(getCallerPackageName())
+                            .build(),
+                    new CallerMetadata.Builder()
+                            .setBinderElapsedTimestamp(SystemClock.elapsedRealtime())
+                            .build(),
+                    new AdSelectionCallback.Stub() {
+                        @Override
+                        public void onSuccess(AdSelectionResponse resultParcel) {
+                            executor.execute(
+                                    () -> {
+                                        if (resultParcel == null) {
+                                            receiver.onResult(AdSelectionOutcome.NO_OUTCOME);
+                                        } else {
+                                            receiver.onResult(
+                                                    new AdSelectionOutcome.Builder()
+                                                            .setAdSelectionId(
+                                                                    resultParcel.getAdSelectionId())
+                                                            .setRenderUri(
+                                                                    resultParcel.getRenderUri())
+                                                            .build());
+                                        }
+                                    });
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () -> {
+                                        receiver.onError(
+                                                AdServicesStatusUtils.asException(failureParcel));
+                                    });
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service.");
+            receiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service.", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Failure of AdSelection service.");
+            receiver.onError(new IllegalStateException("Failure of AdSelection service.", e));
+        }
+    }
+
+    /**
+     * Notifies the service that there is a new impression to report for the ad selected by the
+     * ad-selection run identified by {@code adSelectionId}. There is no guarantee about when the
+     * impression will be reported. The impression reporting could be delayed and reports could be
+     * batched.
+     *
+     * <p>To calculate the winning seller reporting URL, the service fetches the seller's JavaScript
+     * logic from the {@link AdSelectionConfig#getDecisionLogicUri()} found at {@link
+     * ReportImpressionRequest#getAdSelectionConfig()}. Then, the service executes one of the
+     * functions found in the seller JS called {@code reportResult}, providing on-device signals as
+     * well as {@link ReportImpressionRequest#getAdSelectionConfig()} as input parameters.
+     *
+     * <p>The function definition of {@code reportResult} is:
+     *
+     * <p>{@code function reportResult(ad_selection_config, render_url, bid, contextual_signals) {
+     * return { 'status': status, 'results': {'signals_for_buyer': signals_for_buyer,
+     * 'reporting_url': reporting_url } }; } }
+     *
+     * <p>To calculate the winning buyer reporting URL, the service fetches the winning buyer's
+     * JavaScript logic which is fetched via the buyer's {@link
+     * android.adservices.customaudience.CustomAudience#getBiddingLogicUri()}. Then, the service
+     * executes one of the functions found in the buyer JS called {@code reportWin}, providing
+     * on-device signals, {@code signals_for_buyer} calculated by {@code reportResult}, and specific
+     * fields from {@link ReportImpressionRequest#getAdSelectionConfig()} as input parameters.
+     *
+     * <p>The function definition of {@code reportWin} is:
+     *
+     * <p>{@code function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,
+     * contextual_signals, custom_audience_reporting_signals) { return {'status': 0, 'results':
+     * {'reporting_url': reporting_url } }; } }
+     *
+     * <p>In addition, buyers and sellers have the option to register to receive reports on specific
+     * ad events. To do so, they can invoke the platform provided {@code registerAdBeacon} function
+     * inside {@code reportWin} and {@code reportResult} for buyers and sellers, respectively.
+     *
+     * <p>The function definition of {@code registerBeacon} is:
+     *
+     * <p>{@code function registerAdBeacon(beacons)}, where {@code beacons} is a dict of string to
+     * string pairs
+     *
+     * <p>For each ad event a buyer/seller is interested in reports for, they would add an {@code
+     * event_key}: {@code event_reporting_uri} pair to the {@code beacons} dict, where {@code
+     * event_key} is an identifier for that specific event. This {@code event_key} should match
+     * {@link ReportEventRequest#getKey()} when the SDK invokes {@link #reportEvent}. In addition,
+     * each {@code event_reporting_uri} should parse properly into a {@link android.net.Uri}. This
+     * will be the {@link android.net.Uri} reported to when the SDK invokes {@link #reportEvent}.
+     *
+     * <p>When the buyer/seller has added all the pairings they want to receive events for, they can
+     * invoke {@code registerAdBeacon(beacons)}, where {@code beacons} is the name of the dict they
+     * added the pairs to.
+     *
+     * <p>{@code registerAdBeacon} will throw a {@code TypeError} in these situations:
+     *
+     * <ol>
+     *   <li>{@code registerAdBeacon}is called more than once. If this error is caught in
+     *       reportWin/reportResult, the original set of pairings will be registered
+     *   <li>{@code registerAdBeacon} doesn't have exactly 1 dict argument.
+     *   <li>The contents of the 1 dict argument are not all {@code String: String} pairings.
+     * </ol>
+     *
+     * <p>The output is passed by the {@code receiver}, which either returns an empty {@link Object}
+     * for a successful run, or an {@link Exception} includes the type of the exception thrown and
+     * the corresponding error message.
+     *
+     * <p>If the {@link IllegalArgumentException} is thrown, it is caused by invalid input argument
+     * the API received to report the impression.
+     *
+     * <p>If the {@link IllegalStateException} is thrown with error message "Failure of AdSelection
+     * services.", it is caused by an internal failure of the ad selection service.
+     *
+     * <p>If the {@link LimitExceededException} is thrown, it is caused when the calling package
+     * exceeds the allowed rate limits and is throttled.
+     *
+     * <p>If the {@link SecurityException} is thrown, it is caused when the caller is not authorized
+     * or permission is not requested.
+     *
+     * <p>Impressions will be reported at most once as a best-effort attempt.
+     */
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void reportImpression(
+            @NonNull ReportImpressionRequest request,
+            @NonNull Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+
+        try {
+            final AdSelectionService service = getServiceProvider().getService();
+            service.reportImpression(
+                    new ReportImpressionInput.Builder()
+                            .setAdSelectionId(request.getAdSelectionId())
+                            .setAdSelectionConfig(request.getAdSelectionConfig())
+                            .setCallerPackageName(getCallerPackageName())
+                            .build(),
+                    new ReportImpressionCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () -> {
+                                        receiver.onError(
+                                                AdServicesStatusUtils.asException(failureParcel));
+                                    });
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service.");
+            receiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service.", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Failure of AdSelection service.", e));
+        }
+    }
+
+    /**
+     * Notifies the service that there is a new ad event to report for the ad selected by the
+     * ad-selection run identified by {@code adSelectionId}. An ad event is any occurrence that
+     * happens to an ad associated with the given {@code adSelectionId}. There is no guarantee about
+     * when the ad event will be reported. The event reporting could be delayed and reports could be
+     * batched.
+     *
+     * <p>Using {@link ReportEventRequest#getKey()}, the service will fetch the {@code reportingUri}
+     * that was registered in {@code registerAdBeacon}. See documentation of {@link
+     * #reportImpression} for more details regarding {@code registerAdBeacon}. Then, the service
+     * will attach {@link ReportEventRequest#getData()} to the request body of a POST request and
+     * send the request. The body of the POST request will have the {@code content-type} of {@code
+     * text/plain}, and the data will be transmitted in {@code charset=UTF-8}.
+     *
+     * <p>The output is passed by the receiver, which either returns an empty {@link Object} for a
+     * successful run, or an {@link Exception} includes the type of the exception thrown and the
+     * corresponding error message.
+     *
+     * <p>If the {@link IllegalArgumentException} is thrown, it is caused by invalid input argument
+     * the API received to report the ad event.
+     *
+     * <p>If the {@link IllegalStateException} is thrown with error message "Failure of AdSelection
+     * services.", it is caused by an internal failure of the ad selection service.
+     *
+     * <p>If the {@link LimitExceededException} is thrown, it is caused when the calling package
+     * exceeds the allowed rate limits and is throttled.
+     *
+     * <p>If the {@link SecurityException} is thrown, it is caused when the caller is not authorized
+     * or permission is not requested.
+     *
+     * <p>Events will be reported at most once as a best-effort attempt.
+     */
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void reportEvent(
+            @NonNull ReportEventRequest request,
+            @NonNull Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+        try {
+            ReportInteractionInput.Builder inputBuilder =
+                    new ReportInteractionInput.Builder()
+                            .setAdSelectionId(request.getAdSelectionId())
+                            .setInteractionKey(request.getKey())
+                            .setInteractionData(request.getData())
+                            .setReportingDestinations(request.getReportingDestinations())
+                            .setCallerPackageName(getCallerPackageName())
+                            .setCallerSdkName(getCallerSdkName())
+                            .setInputEvent(request.getInputEvent());
+
+            getAdId((adIdValue) -> inputBuilder.setAdId(adIdValue));
+
+            final AdSelectionService service = getServiceProvider().getService();
+            service.reportInteraction(
+                    inputBuilder.build(),
+                    new ReportInteractionCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () -> {
+                                        receiver.onError(
+                                                AdServicesStatusUtils.asException(failureParcel));
+                                    });
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service.");
+            receiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service.", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Failure of AdSelection service.", e));
+        }
+    }
+
+    /**
+     * Gives the provided list of adtechs the ability to do app install filtering on the calling
+     * app.
+     *
+     * <p>The input {@code request} is provided by the Ads SDK and the {@code request} object is
+     * transferred via a Binder call. For this reason, the total size of these objects is bound to
+     * the Android IPC limitations. Failures to transfer the {@code advertisers} will throws an
+     * {@link TransactionTooLargeException}.
+     *
+     * <p>The output is passed by the receiver, which either returns an empty {@link Object} for a
+     * successful run, or an {@link Exception} includes the type of the exception thrown and the
+     * corresponding error message.
+     *
+     * <p>If the {@link IllegalArgumentException} is thrown, it is caused by invalid input argument
+     * the API received.
+     *
+     * <p>If the {@link IllegalStateException} is thrown with error message "Failure of AdSelection
+     * services.", it is caused by an internal failure of the ad selection service.
+     *
+     * <p>If the {@link LimitExceededException} is thrown, it is caused when the calling package
+     * exceeds the allowed rate limits and is throttled.
+     *
+     * <p>If the {@link SecurityException} is thrown, it is caused when the caller is not authorized
+     * or permission is not requested.
+     */
+    @FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void setAppInstallAdvertisers(
+            @NonNull SetAppInstallAdvertisersRequest request,
+            @NonNull Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+
+        try {
+            final AdSelectionService service = getServiceProvider().getService();
+            service.setAppInstallAdvertisers(
+                    new SetAppInstallAdvertisersInput.Builder()
+                            .setAdvertisers(request.getAdvertisers())
+                            .setCallerPackageName(getCallerPackageName())
+                            .build(),
+                    new SetAppInstallAdvertisersCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () -> {
+                                        receiver.onError(
+                                                AdServicesStatusUtils.asException(failureParcel));
+                                    });
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service.");
+            receiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service.", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Failure of AdSelection service.", e));
+        }
+    }
+
+    /**
+     * Updates the counter histograms for an ad which was previously selected by a call to {@link
+     * #selectAds(AdSelectionConfig, Executor, OutcomeReceiver)}.
+     *
+     * <p>The counter histograms are used in ad selection to inform frequency cap filtering on
+     * candidate ads, where ads whose frequency caps are met or exceeded are removed from the
+     * bidding process during ad selection.
+     *
+     * <p>Counter histograms can only be updated for ads specified by the given {@code
+     * adSelectionId} returned by a recent call to FLEDGE ad selection from the same caller app.
+     *
+     * <p>A {@link SecurityException} is returned via the {@code outcomeReceiver} if:
+     *
+     * <ol>
+     *   <li>the app has not declared the correct permissions in its manifest, or
+     *   <li>the app or entity identified by the {@code callerAdTechIdentifier} are not authorized
+     *       to use the API.
+     * </ol>
+     *
+     * An {@link IllegalStateException} is returned via the {@code outcomeReceiver} if the call does
+     * not come from an app with a foreground activity.
+     *
+     * <p>A {@link LimitExceededException} is returned via the {@code outcomeReceiver} if the call
+     * exceeds the calling app's API throttle.
+     *
+     * <p>In all other failure cases, the {@code outcomeReceiver} will return an empty {@link
+     * Object}. Note that to protect user privacy, internal errors will not be sent back via an
+     * exception.
+     */
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void updateAdCounterHistogram(
+            @NonNull UpdateAdCounterHistogramRequest updateAdCounterHistogramRequest,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> outcomeReceiver) {
+        Objects.requireNonNull(updateAdCounterHistogramRequest, "Request must not be null");
+        Objects.requireNonNull(executor, "Executor must not be null");
+        Objects.requireNonNull(outcomeReceiver, "Outcome receiver must not be null");
+
+        try {
+            final AdSelectionService service = getServiceProvider().getService();
+            Objects.requireNonNull(service);
+            service.updateAdCounterHistogram(
+                    new UpdateAdCounterHistogramInput.Builder(
+                                    updateAdCounterHistogramRequest.getAdSelectionId(),
+                                    updateAdCounterHistogramRequest.getAdEventType(),
+                                    updateAdCounterHistogramRequest.getCallerAdTech(),
+                                    getCallerPackageName())
+                            .build(),
+                    new UpdateAdCounterHistogramCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> outcomeReceiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () -> {
+                                        outcomeReceiver.onError(
+                                                AdServicesStatusUtils.asException(failureParcel));
+                                    });
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service");
+            outcomeReceiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Remote exception encountered while updating ad counter histogram");
+            outcomeReceiver.onError(new IllegalStateException("Failure of AdSelection service", e));
+        }
+    }
+
+    private String getCallerPackageName() {
+        SandboxedSdkContext sandboxedSdkContext =
+                SandboxedSdkContextUtils.getAsSandboxedSdkContext(mContext);
+        return sandboxedSdkContext == null
+                ? mContext.getPackageName()
+                : sandboxedSdkContext.getClientPackageName();
+    }
+
+    private byte[] getAdSelectionData(GetAdSelectionDataResponse response) throws IOException {
+        if (Objects.nonNull(response.getAssetFileDescriptor())) {
+            AssetFileDescriptor assetFileDescriptor = response.getAssetFileDescriptor();
+            return AssetFileDescriptorUtil.readAssetFileDescriptorIntoBuffer(assetFileDescriptor);
+        } else {
+            return response.getAdSelectionData();
+        }
+    }
+
+    private String getCallerSdkName() {
+        SandboxedSdkContext sandboxedSdkContext =
+                SandboxedSdkContextUtils.getAsSandboxedSdkContext(mContext);
+        return sandboxedSdkContext == null ? "" : sandboxedSdkContext.getSdkPackageName();
+    }
+
+    private interface AdSelectionAdIdCallback {
+        void onResult(@Nullable String adIdValue);
+    }
+
+    @SuppressLint("MissingPermission")
+    private void getAdId(AdSelectionAdIdCallback adSelectionAdIdCallback) {
+        try {
+            CountDownLatch timer = new CountDownLatch(1);
+            AtomicReference<String> adIdValue = new AtomicReference<>();
+            mAdIdManager.getAdId(
+                    mAdIdExecutor,
+                    new android.adservices.common.AdServicesOutcomeReceiver<>() {
+                        @Override
+                        public void onResult(AdId adId) {
+                            String id = adId.getAdId();
+                            adIdValue.set(!AdId.ZERO_OUT.equals(id) ? id : null);
+                            sLogger.v("AdId permission enabled: %b.", !AdId.ZERO_OUT.equals(id));
+                            timer.countDown();
+                        }
+
+                        @Override
+                        public void onError(Exception e) {
+                            if (e instanceof IllegalStateException
+                                    || e instanceof SecurityException) {
+                                sLogger.w(DEBUG_API_WARNING_MESSAGE);
+                            } else {
+                                sLogger.w(e, DEBUG_API_WARNING_MESSAGE);
+                            }
+                            timer.countDown();
+                        }
+                    });
+
+            boolean timedOut = false;
+            try {
+                timedOut = !timer.await(AD_ID_TIMEOUT_MS, MILLISECONDS);
+            } catch (InterruptedException e) {
+                sLogger.w(e, "Interrupted while getting the AdId.");
+            }
+            if (timedOut) {
+                sLogger.w("AdId call timed out.");
+            }
+            adSelectionAdIdCallback.onResult(adIdValue.get());
+        } catch (Exception e) {
+            sLogger.d(e, "Could not get AdId.");
+            adSelectionAdIdCallback.onResult(null);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/AdSelectionOutcome.java b/android-35/android/adservices/adselection/AdSelectionOutcome.java
new file mode 100644
index 0000000..1568711
--- /dev/null
+++ b/android-35/android/adservices/adselection/AdSelectionOutcome.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.OutcomeReceiver;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * This class represents a field in the {@code OutcomeReceiver}, which is an input to the {@link
+ * AdSelectionManager#selectAds} in the {@link AdSelectionManager}. This field is populated in the
+ * case of a successful {@link AdSelectionManager#selectAds} call.
+ *
+ * <p>Empty outcome may be returned from {@link
+ * AdSelectionManager#selectAds(AdSelectionFromOutcomesConfig, Executor, OutcomeReceiver)}. Use
+ * {@link AdSelectionOutcome#hasOutcome()} to check if an instance has a valid outcome. When {@link
+ * AdSelectionOutcome#hasOutcome()} returns {@code false}, results from {@link AdSelectionOutcome
+ * #getAdSelectionId()} and {@link AdSelectionOutcome#getRenderUri()} are invalid and shouldn't be
+ * used.
+ */
+public class AdSelectionOutcome {
+    /** Represents an AdSelectionOutcome with empty results. */
+    @NonNull public static final AdSelectionOutcome NO_OUTCOME = new AdSelectionOutcome();
+
+    /** @hide */
+    public static final String UNSET_AD_SELECTION_ID_MESSAGE =
+            "Non-zero ad selection ID must be set";
+
+    /** @hide */
+    public static final int UNSET_AD_SELECTION_ID = 0;
+
+    private final long mAdSelectionId;
+    @NonNull private final Uri mRenderUri;
+
+    private AdSelectionOutcome() {
+        mAdSelectionId = UNSET_AD_SELECTION_ID;
+        mRenderUri = Uri.EMPTY;
+    }
+
+    private AdSelectionOutcome(long adSelectionId, @NonNull Uri renderUri) {
+        Objects.requireNonNull(renderUri);
+
+        mAdSelectionId = adSelectionId;
+        mRenderUri = renderUri;
+    }
+
+    /** Returns the renderUri that the AdSelection returns. */
+    @NonNull
+    public Uri getRenderUri() {
+        return mRenderUri;
+    }
+
+    /** Returns the adSelectionId that identifies the AdSelection. */
+    @NonNull
+    public long getAdSelectionId() {
+        return mAdSelectionId;
+    }
+
+    /**
+     * Returns whether the outcome contains results or empty. Empty outcomes' {@code render uris}
+     * shouldn't be used.
+     */
+    public boolean hasOutcome() {
+        return !this.equals(NO_OUTCOME);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof AdSelectionOutcome) {
+            AdSelectionOutcome adSelectionOutcome = (AdSelectionOutcome) o;
+            return mAdSelectionId == adSelectionOutcome.mAdSelectionId
+                    && Objects.equals(mRenderUri, adSelectionOutcome.mRenderUri);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAdSelectionId, mRenderUri);
+    }
+
+    /**
+     * Builder for {@link AdSelectionOutcome} objects.
+     */
+    public static final class Builder {
+        private long mAdSelectionId = UNSET_AD_SELECTION_ID;
+        @Nullable private Uri mRenderUri;
+
+        public Builder() {}
+
+        /** Sets the mAdSelectionId. */
+        @NonNull
+        public AdSelectionOutcome.Builder setAdSelectionId(long adSelectionId) {
+            this.mAdSelectionId = adSelectionId;
+            return this;
+        }
+
+        /** Sets the RenderUri. */
+        @NonNull
+        public AdSelectionOutcome.Builder setRenderUri(@NonNull Uri renderUri) {
+            Objects.requireNonNull(renderUri);
+
+            mRenderUri = renderUri;
+            return this;
+        }
+
+        /**
+         * Builds a {@link AdSelectionOutcome} instance.
+         *
+         * @throws IllegalArgumentException if the adSelectionIid is not set
+         * @throws NullPointerException if the RenderUri is null
+         */
+        @NonNull
+        public AdSelectionOutcome build() {
+            Objects.requireNonNull(mRenderUri);
+
+            Preconditions.checkArgument(
+                    mAdSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+
+            return new AdSelectionOutcome(mAdSelectionId, mRenderUri);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/AdSelectionResponse.java b/android-35/android/adservices/adselection/AdSelectionResponse.java
new file mode 100644
index 0000000..c1a0095
--- /dev/null
+++ b/android-35/android/adservices/adselection/AdSelectionResponse.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID;
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID_MESSAGE;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * This class represents the response returned by the {@link AdSelectionManager} as the result of a
+ * successful {@code selectAds} call.
+ *
+ * @hide
+ */
+public final class AdSelectionResponse implements Parcelable {
+    private final long mAdSelectionId;
+    @NonNull private final Uri mRenderUri;
+
+    private AdSelectionResponse(long adSelectionId, @NonNull Uri renderUri) {
+        Objects.requireNonNull(renderUri);
+
+        mAdSelectionId = adSelectionId;
+        mRenderUri = renderUri;
+    }
+
+    private AdSelectionResponse(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        mAdSelectionId = in.readLong();
+        mRenderUri = Uri.CREATOR.createFromParcel(in);
+    }
+
+    @NonNull
+    public static final Creator<AdSelectionResponse> CREATOR =
+            new Parcelable.Creator<AdSelectionResponse>() {
+                @Override
+                public AdSelectionResponse createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new AdSelectionResponse(in);
+                }
+
+                @Override
+                public AdSelectionResponse[] newArray(int size) {
+                    return new AdSelectionResponse[size];
+                }
+            };
+
+    /** Returns the renderUri that the AdSelection returns. */
+    @NonNull
+    public Uri getRenderUri() {
+        return mRenderUri;
+    }
+
+    /** Returns the adSelectionId that identifies the AdSelection. */
+    @NonNull
+    public long getAdSelectionId() {
+        return mAdSelectionId;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof AdSelectionResponse) {
+            AdSelectionResponse adSelectionResponse = (AdSelectionResponse) o;
+            return mAdSelectionId == adSelectionResponse.mAdSelectionId
+                    && Objects.equals(mRenderUri, adSelectionResponse.mRenderUri);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAdSelectionId, mRenderUri);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        dest.writeLong(mAdSelectionId);
+        mRenderUri.writeToParcel(dest, flags);
+    }
+
+    @Override
+    public String toString() {
+        return "AdSelectionResponse{"
+                + "mAdSelectionId="
+                + mAdSelectionId
+                + ", mRenderUri="
+                + mRenderUri
+                + '}';
+    }
+
+    /**
+     * Builder for {@link AdSelectionResponse} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private long mAdSelectionId = UNSET_AD_SELECTION_ID;
+        @NonNull private Uri mRenderUri;
+
+        public Builder() {}
+
+        /** Sets the mAdSelectionId. */
+        @NonNull
+        public AdSelectionResponse.Builder setAdSelectionId(long adSelectionId) {
+            this.mAdSelectionId = adSelectionId;
+            return this;
+        }
+
+        /** Sets the RenderUri. */
+        @NonNull
+        public AdSelectionResponse.Builder setRenderUri(@NonNull Uri renderUri) {
+            Objects.requireNonNull(renderUri);
+
+            mRenderUri = renderUri;
+            return this;
+        }
+
+        /**
+         * Builds a {@link AdSelectionResponse} instance.
+         *
+         * @throws IllegalArgumentException if the adSelectionIid is not set
+         * @throws NullPointerException if the RenderUri is null
+         */
+        @NonNull
+        public AdSelectionResponse build() {
+            Objects.requireNonNull(mRenderUri);
+
+            Preconditions.checkArgument(
+                    mAdSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+
+            return new AdSelectionResponse(mAdSelectionId, mRenderUri);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/AdWithBid.java b/android-35/android/adservices/adselection/AdWithBid.java
new file mode 100644
index 0000000..af53bd0
--- /dev/null
+++ b/android-35/android/adservices/adselection/AdWithBid.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import android.adservices.common.AdData;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.flags.Flags;
+
+import java.util.Objects;
+
+/**
+ * Represents an ad and its corresponding bid value after the bid generation step in the ad
+ * selection process.
+ *
+ * <p>The ads and their bids are fed into an ad scoring process which will inform the final ad
+ * selection. The currency unit for the bid is expected to be the same requested by the seller when
+ * initiating the selection process and not specified in this class. The seller can provide the
+ * currency via AdSelectionSignals. The currency is opaque to FLEDGE.
+ */
+@FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
+public final class AdWithBid implements Parcelable {
+    @NonNull
+    private final AdData mAdData;
+    private final double mBid;
+
+    @NonNull
+    public static final Creator<AdWithBid> CREATOR =
+            new Creator<AdWithBid>() {
+                @Override
+                public AdWithBid createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+
+                    return new AdWithBid(in);
+                }
+
+                @Override
+                public AdWithBid[] newArray(int size) {
+                    return new AdWithBid[size];
+                }
+            };
+
+    /**
+     * @param adData An {@link AdData} object defining an ad's render URI and buyer metadata
+     * @param bid The amount of money a buyer has bid to show an ad; note that while the bid is
+     *     expected to be non-negative, this is only enforced during the ad selection process
+     * @throws NullPointerException if adData is null
+     */
+    public AdWithBid(@NonNull AdData adData, double bid) {
+        Objects.requireNonNull(adData);
+        mAdData = adData;
+        mBid = bid;
+    }
+
+    private AdWithBid(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+        mAdData = AdData.CREATOR.createFromParcel(in);
+        mBid = in.readDouble();
+    }
+
+    /**
+     * @return the ad that was bid on
+     */
+    @NonNull
+    public AdData getAdData() {
+        return mAdData;
+    }
+
+    /**
+     * The bid is the amount of money an advertiser has bid during the ad selection process to show
+     * an ad. The bid could be any non-negative {@code double}, such as 0.00, 0.17, 1.10, or
+     * 1000.00.
+     *
+     * <p>The currency for a bid would be controlled by Seller and will remain consistent across a
+     * run of Ad selection. This could be achieved by leveraging bidding signals during
+     * "generateBid()" phase and using the same currency during the creation of contextual ads.
+     * Having currency unit as a dedicated field could be supported in future releases.
+     *
+     * @return the bid value to be passed to the scoring function when scoring the ad returned by
+     *     {@link #getAdData()}
+     */
+    public double getBid() {
+        return mBid;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        mAdData.writeToParcel(dest, flags);
+        dest.writeDouble(mBid);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AdWithBid)) return false;
+        AdWithBid adWithBid = (AdWithBid) o;
+        return Double.compare(adWithBid.mBid, mBid) == 0
+                && Objects.equals(mAdData, adWithBid.mAdData);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAdData, mBid);
+    }
+}
diff --git a/android-35/android/adservices/adselection/AddAdSelectionFromOutcomesOverrideRequest.java b/android-35/android/adservices/adselection/AddAdSelectionFromOutcomesOverrideRequest.java
new file mode 100644
index 0000000..7e4f97f
--- /dev/null
+++ b/android-35/android/adservices/adselection/AddAdSelectionFromOutcomesOverrideRequest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import android.adservices.common.AdSelectionSignals;
+import android.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * This POJO represents the {@link
+ * TestAdSelectionManager#overrideAdSelectionFromOutcomesConfigRemoteInfo} (
+ * AddAdSelectionOverrideRequest, Executor, OutcomeReceiver)} request
+ *
+ * <p>It contains, a {@link AdSelectionFromOutcomesConfig} which will serve as the identifier for
+ * the specific override, a {@code String} selectionLogicJs and {@code String} selectionSignals
+ * field representing the override value
+ *
+ */
+public class AddAdSelectionFromOutcomesOverrideRequest {
+    @NonNull private final AdSelectionFromOutcomesConfig mAdSelectionFromOutcomesConfig;
+
+    @NonNull private final String mOutcomeSelectionLogicJs;
+
+    @NonNull private final AdSelectionSignals mOutcomeSelectionTrustedSignals;
+
+    /** Builds a {@link AddAdSelectionFromOutcomesOverrideRequest} instance. */
+    public AddAdSelectionFromOutcomesOverrideRequest(
+            @NonNull AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig,
+            @NonNull String outcomeSelectionLogicJs,
+            @NonNull AdSelectionSignals outcomeSelectionTrustedSignals) {
+        Objects.requireNonNull(adSelectionFromOutcomesConfig);
+        Objects.requireNonNull(outcomeSelectionLogicJs);
+        Objects.requireNonNull(outcomeSelectionTrustedSignals);
+
+        mAdSelectionFromOutcomesConfig = adSelectionFromOutcomesConfig;
+        mOutcomeSelectionLogicJs = outcomeSelectionLogicJs;
+        mOutcomeSelectionTrustedSignals = outcomeSelectionTrustedSignals;
+    }
+
+    /**
+     * @return an instance of {@link AdSelectionFromOutcomesConfig}, the configuration of the ad
+     *     selection process. This configuration provides the data necessary to run Ad Selection
+     *     flow that generates bids and scores to find a wining ad for rendering.
+     */
+    @NonNull
+    public AdSelectionFromOutcomesConfig getAdSelectionFromOutcomesConfig() {
+        return mAdSelectionFromOutcomesConfig;
+    }
+
+    /**
+     * @return The override javascript result, should be a string that contains valid JS code. The
+     *     code should contain the outcome selection logic that will be executed during ad outcome
+     *     selection.
+     */
+    @NonNull
+    public String getOutcomeSelectionLogicJs() {
+        return mOutcomeSelectionLogicJs;
+    }
+
+    /**
+     * @return The override trusted scoring signals, should be a valid json string. The trusted
+     *     signals would be fed into the outcome selection logic during ad outcome selection.
+     */
+    @NonNull
+    public AdSelectionSignals getOutcomeSelectionTrustedSignals() {
+        return mOutcomeSelectionTrustedSignals;
+    }
+}
diff --git a/android-35/android/adservices/adselection/AddAdSelectionOverrideRequest.java b/android-35/android/adservices/adselection/AddAdSelectionOverrideRequest.java
new file mode 100644
index 0000000..f537643
--- /dev/null
+++ b/android-35/android/adservices/adselection/AddAdSelectionOverrideRequest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import android.adservices.common.AdSelectionSignals;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.OutcomeReceiver;
+
+import com.android.adservices.flags.Flags;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * This POJO represents the {@link
+ * TestAdSelectionManager#overrideAdSelectionConfigRemoteInfo(AddAdSelectionOverrideRequest,
+ * Executor, OutcomeReceiver)} request
+ *
+ * <p>It contains, a {@link AdSelectionConfig} which will serve as the identifier for the specific
+ * override, a {@code String} decisionLogicJs and {@code String} trustedScoringSignals field
+ * representing the override value
+ */
+public class AddAdSelectionOverrideRequest {
+    @NonNull private final AdSelectionConfig mAdSelectionConfig;
+
+    @NonNull private final String mDecisionLogicJs;
+
+    @NonNull private final AdSelectionSignals mTrustedScoringSignals;
+
+    @NonNull private final PerBuyerDecisionLogic mPerBuyerDecisionLogic;
+
+    /**
+     * Builds a {@link AddAdSelectionOverrideRequest} instance.
+     *
+     * @param adSelectionConfig configuration for ad selection. See {@link AdSelectionConfig}
+     * @param decisionLogicJs override for scoring logic. See {@link
+     *     AdSelectionConfig#getDecisionLogicUri()}
+     * @param trustedScoringSignals override for trusted seller signals. See {@link
+     *     AdSelectionConfig#getTrustedScoringSignalsUri()}
+     * @param perBuyerDecisionLogic override for buyer's reporting logic for contextual ads. See
+     *     {@link SignedContextualAds#getDecisionLogicUri()}
+     */
+    @FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
+    public AddAdSelectionOverrideRequest(
+            @NonNull AdSelectionConfig adSelectionConfig,
+            @NonNull String decisionLogicJs,
+            @NonNull AdSelectionSignals trustedScoringSignals,
+            @NonNull PerBuyerDecisionLogic perBuyerDecisionLogic) {
+        Objects.requireNonNull(adSelectionConfig);
+        Objects.requireNonNull(decisionLogicJs);
+        Objects.requireNonNull(trustedScoringSignals);
+        Objects.requireNonNull(perBuyerDecisionLogic);
+
+        mAdSelectionConfig = adSelectionConfig;
+        mDecisionLogicJs = decisionLogicJs;
+        mTrustedScoringSignals = trustedScoringSignals;
+        mPerBuyerDecisionLogic = perBuyerDecisionLogic;
+    }
+
+    /**
+     * Builds a {@link AddAdSelectionOverrideRequest} instance.
+     *
+     * @param adSelectionConfig configuration for ad selection. See {@link AdSelectionConfig}
+     * @param decisionLogicJs override for scoring logic. See {@link
+     *     AdSelectionConfig#getDecisionLogicUri()}
+     * @param trustedScoringSignals override for trusted seller signals. See {@link
+     *     AdSelectionConfig#getTrustedScoringSignalsUri()}
+     */
+    public AddAdSelectionOverrideRequest(
+            @NonNull AdSelectionConfig adSelectionConfig,
+            @NonNull String decisionLogicJs,
+            @NonNull AdSelectionSignals trustedScoringSignals) {
+        this(
+                adSelectionConfig,
+                decisionLogicJs,
+                trustedScoringSignals,
+                PerBuyerDecisionLogic.EMPTY);
+    }
+
+    /**
+     * @return an instance of {@link AdSelectionConfig}, the configuration of the ad selection
+     *     process. This configuration provides the data necessary to run Ad Selection flow that
+     *     generates bids and scores to find a wining ad for rendering.
+     */
+    @NonNull
+    public AdSelectionConfig getAdSelectionConfig() {
+        return mAdSelectionConfig;
+    }
+
+    /**
+     * @return The override javascript result, should be a string that contains valid JS code. The
+     *     code should contain the scoring logic that will be executed during Ad selection.
+     */
+    @NonNull
+    public String getDecisionLogicJs() {
+        return mDecisionLogicJs;
+    }
+
+    /**
+     * @return The override trusted scoring signals, should be a valid json string. The trusted
+     *     signals would be fed into the scoring logic during Ad Selection.
+     */
+    @NonNull
+    public AdSelectionSignals getTrustedScoringSignals() {
+        return mTrustedScoringSignals;
+    }
+
+    /**
+     * @return The override for the decision logic for each buyer that is used by contextual ads for
+     *     reporting, which may be extended to updating bid values for contextual ads in the future
+     */
+    @FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
+    @NonNull
+    public PerBuyerDecisionLogic getPerBuyerDecisionLogic() {
+        return mPerBuyerDecisionLogic;
+    }
+}
diff --git a/android-35/android/adservices/adselection/DecisionLogic.java b/android-35/android/adservices/adselection/DecisionLogic.java
new file mode 100644
index 0000000..1cb2680
--- /dev/null
+++ b/android-35/android/adservices/adselection/DecisionLogic.java
@@ -0,0 +1,91 @@
+/*
+ * 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 android.adservices.adselection;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.flags.Flags;
+
+import java.util.Objects;
+
+/** Generic Decision logic that could be provided by the buyer or seller. */
+@FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
+public final class DecisionLogic implements Parcelable {
+
+    @NonNull private String mDecisionLogic;
+
+    public DecisionLogic(@NonNull String buyerDecisionLogic) {
+        Objects.requireNonNull(buyerDecisionLogic);
+        mDecisionLogic = buyerDecisionLogic;
+    }
+
+    private DecisionLogic(@NonNull Parcel in) {
+        this(in.readString());
+    }
+
+    @NonNull
+    public static final Creator<DecisionLogic> CREATOR =
+            new Creator<DecisionLogic>() {
+                @Override
+                public DecisionLogic createFromParcel(Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new DecisionLogic(in);
+                }
+
+                @Override
+                public DecisionLogic[] newArray(int size) {
+                    return new DecisionLogic[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+        dest.writeString(mDecisionLogic);
+    }
+
+    @Override
+    public String toString() {
+        return mDecisionLogic;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDecisionLogic);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof DecisionLogic)) return false;
+        DecisionLogic decisionLogic = (DecisionLogic) o;
+        return mDecisionLogic.equals(decisionLogic.getLogic());
+    }
+
+    @NonNull
+    public String getLogic() {
+        return mDecisionLogic;
+    }
+}
diff --git a/android-35/android/adservices/adselection/GetAdSelectionDataInput.java b/android-35/android/adservices/adselection/GetAdSelectionDataInput.java
new file mode 100644
index 0000000..98862a5
--- /dev/null
+++ b/android-35/android/adservices/adselection/GetAdSelectionDataInput.java
@@ -0,0 +1,184 @@
+/*
+ * 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 android.adservices.adselection;
+
+import android.adservices.common.AdTechIdentifier;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.AdServicesParcelableUtil;
+
+import java.util.Objects;
+
+/**
+ * Represent input params to the GetAdSelectionData API.
+ *
+ * @hide
+ */
+public final class GetAdSelectionDataInput implements Parcelable {
+    @Nullable private final AdTechIdentifier mSeller;
+    @NonNull private final String mCallerPackageName;
+
+    @Nullable private final Uri mCoordinatorOriginUri;
+
+    @NonNull
+    public static final Creator<GetAdSelectionDataInput> CREATOR =
+            new Creator<>() {
+                public GetAdSelectionDataInput createFromParcel(Parcel in) {
+                    return new GetAdSelectionDataInput(in);
+                }
+
+                public GetAdSelectionDataInput[] newArray(int size) {
+                    return new GetAdSelectionDataInput[size];
+                }
+            };
+
+    private GetAdSelectionDataInput(
+            @Nullable AdTechIdentifier seller,
+            @NonNull String callerPackageName,
+            @Nullable Uri coordinatorOriginUri) {
+        Objects.requireNonNull(callerPackageName);
+
+        this.mSeller = seller;
+        this.mCallerPackageName = callerPackageName;
+        this.mCoordinatorOriginUri = coordinatorOriginUri;
+    }
+
+    private GetAdSelectionDataInput(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        this.mSeller =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, AdTechIdentifier.CREATOR::createFromParcel);
+        this.mCallerPackageName = in.readString();
+        this.mCoordinatorOriginUri =
+                AdServicesParcelableUtil.readNullableFromParcel(in, Uri.CREATOR::createFromParcel);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof GetAdSelectionDataInput) {
+            GetAdSelectionDataInput obj = (GetAdSelectionDataInput) o;
+            return Objects.equals(mSeller, obj.mSeller)
+                    && Objects.equals(mCallerPackageName, obj.mCallerPackageName)
+                    && Objects.equals(mCoordinatorOriginUri, obj.mCoordinatorOriginUri);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSeller, mCallerPackageName, mCoordinatorOriginUri);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mSeller,
+                (targetParcel, sourceSignals) -> sourceSignals.writeToParcel(targetParcel, flags));
+        dest.writeString(mCallerPackageName);
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mCoordinatorOriginUri,
+                (targetParcel, sourceOrigin) -> sourceOrigin.writeToParcel(targetParcel, flags));
+    }
+
+    /**
+     * @return a AdTechIdentifier of the seller, for example "www.example-ssp.com"
+     */
+    @Nullable
+    public AdTechIdentifier getSeller() {
+        return mSeller;
+    }
+
+    /**
+     * @return the caller package name
+     */
+    @NonNull
+    public String getCallerPackageName() {
+        return mCallerPackageName;
+    }
+
+    /**
+     * @return the caller package name
+     */
+    @Nullable
+    public Uri getCoordinatorOriginUri() {
+        return mCoordinatorOriginUri;
+    }
+
+    /**
+     * Builder for {@link GetAdSelectionDataInput} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        @Nullable private AdTechIdentifier mSeller;
+        @Nullable private String mCallerPackageName;
+        @Nullable private Uri mCoordinatorOrigin;
+
+        public Builder() {}
+
+        /** Sets the seller {@link AdTechIdentifier}. */
+        @NonNull
+        public GetAdSelectionDataInput.Builder setSeller(@Nullable AdTechIdentifier seller) {
+            this.mSeller = seller;
+            return this;
+        }
+
+        /** Sets the caller's package name. */
+        @NonNull
+        public GetAdSelectionDataInput.Builder setCallerPackageName(
+                @NonNull String callerPackageName) {
+            Objects.requireNonNull(callerPackageName);
+
+            this.mCallerPackageName = callerPackageName;
+            return this;
+        }
+
+        /** Sets the coordinator origin URI . */
+        @NonNull
+        public GetAdSelectionDataInput.Builder setCoordinatorOriginUri(
+                @Nullable Uri coordinatorOrigin) {
+            this.mCoordinatorOrigin = coordinatorOrigin;
+            return this;
+        }
+
+        /**
+         * Builds a {@link GetAdSelectionDataInput} instance.
+         *
+         * @throws NullPointerException if the CallerPackageName is null
+         */
+        @NonNull
+        public GetAdSelectionDataInput build() {
+            Objects.requireNonNull(mCallerPackageName);
+
+            return new GetAdSelectionDataInput(mSeller, mCallerPackageName, mCoordinatorOrigin);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/GetAdSelectionDataOutcome.java b/android-35/android/adservices/adselection/GetAdSelectionDataOutcome.java
new file mode 100644
index 0000000..c721e56
--- /dev/null
+++ b/android-35/android/adservices/adselection/GetAdSelectionDataOutcome.java
@@ -0,0 +1,111 @@
+/*
+ * 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 android.adservices.adselection;
+
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID;
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID_MESSAGE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.Nullable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/** Represents ad selection data collected from device for ad selection. */
+public final class GetAdSelectionDataOutcome {
+    private final long mAdSelectionId;
+    @Nullable private final byte[] mAdSelectionData;
+
+    private GetAdSelectionDataOutcome(long adSelectionId, @Nullable byte[] adSelectionData) {
+        this.mAdSelectionId = adSelectionId;
+        this.mAdSelectionData = adSelectionData;
+    }
+
+    /**
+     * Returns the adSelectionId that identifies the AdSelection.
+     *
+     * @deprecated Use the {@link #getAdSelectionDataId()} instead.
+     */
+    public long getAdSelectionId() {
+        return mAdSelectionId;
+    }
+
+    /** Returns the id that uniquely identifies this GetAdSelectionData payload. */
+    @FlaggedApi(
+            "com.android.adservices.flags.fledge_auction_server_get_ad_selection_data_id_enabled")
+    public long getAdSelectionDataId() {
+        return mAdSelectionId;
+    }
+
+    /** Returns the adSelectionData that is collected from device. */
+    @Nullable
+    public byte[] getAdSelectionData() {
+        if (Objects.isNull(mAdSelectionData)) {
+            return null;
+        } else {
+            return Arrays.copyOf(mAdSelectionData, mAdSelectionData.length);
+        }
+    }
+
+    /**
+     * Builder for {@link GetAdSelectionDataOutcome} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private long mAdSelectionId;
+        @Nullable private byte[] mAdSelectionData;
+
+        public Builder() {}
+
+        /** Sets the adSelectionId. */
+        @NonNull
+        public GetAdSelectionDataOutcome.Builder setAdSelectionId(long adSelectionId) {
+            this.mAdSelectionId = adSelectionId;
+            return this;
+        }
+
+        /** Sets the adSelectionData. */
+        @NonNull
+        public GetAdSelectionDataOutcome.Builder setAdSelectionData(
+                @Nullable byte[] adSelectionData) {
+            if (!Objects.isNull(adSelectionData)) {
+                this.mAdSelectionData = Arrays.copyOf(adSelectionData, adSelectionData.length);
+            } else {
+                this.mAdSelectionData = null;
+            }
+            return this;
+        }
+
+        /**
+         * Builds a {@link GetAdSelectionDataOutcome} instance.
+         *
+         * @throws IllegalArgumentException if the adSelectionId is not set
+         */
+        @NonNull
+        public GetAdSelectionDataOutcome build() {
+            Preconditions.checkArgument(
+                    mAdSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+
+            return new GetAdSelectionDataOutcome(mAdSelectionId, mAdSelectionData);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/GetAdSelectionDataRequest.java b/android-35/android/adservices/adselection/GetAdSelectionDataRequest.java
new file mode 100644
index 0000000..362ad83
--- /dev/null
+++ b/android-35/android/adservices/adselection/GetAdSelectionDataRequest.java
@@ -0,0 +1,106 @@
+/*
+ * 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 android.adservices.adselection;
+
+import android.adservices.common.AdTechIdentifier;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+
+import com.android.adservices.flags.Flags;
+
+/**
+ * Represents a request containing the information to get ad selection data.
+ *
+ * <p>Instances of this class are created by SDKs to be provided as arguments to the {@link
+ * AdSelectionManager#getAdSelectionData} methods in {@link AdSelectionManager}.
+ */
+public final class GetAdSelectionDataRequest {
+    @Nullable private final AdTechIdentifier mSeller;
+
+    @Nullable private final Uri mCoordinatorOriginUri;
+
+    private GetAdSelectionDataRequest(
+            @Nullable AdTechIdentifier seller, @Nullable Uri coordinatorOriginUri) {
+        this.mSeller = seller;
+        this.mCoordinatorOriginUri = coordinatorOriginUri;
+    }
+
+    /**
+     * @return a AdTechIdentifier of the seller, for example "www.example-ssp.com"
+     */
+    @Nullable
+    public AdTechIdentifier getSeller() {
+        return mSeller;
+    }
+
+    /**
+     * @return the coordinator origin Uri where the public keys for encryption are fetched from
+     *     <p>See {@link Builder#setCoordinatorOriginUri(Uri)} for more details on the coordinator
+     *     origin
+     */
+    @Nullable
+    @FlaggedApi(Flags.FLAG_FLEDGE_SERVER_AUCTION_MULTI_CLOUD_ENABLED)
+    public Uri getCoordinatorOriginUri() {
+        return mCoordinatorOriginUri;
+    }
+
+    /**
+     * Builder for {@link GetAdSelectionDataRequest} objects.
+     */
+    public static final class Builder {
+        @Nullable private AdTechIdentifier mSeller;
+
+        @Nullable private Uri mCoordinatorOriginUri;
+
+        public Builder() {}
+
+        /** Sets the seller {@link AdTechIdentifier}. */
+        @NonNull
+        public GetAdSelectionDataRequest.Builder setSeller(@Nullable AdTechIdentifier seller) {
+            this.mSeller = seller;
+            return this;
+        }
+
+        /**
+         * Sets the coordinator origin from which PPAPI should fetch the public key for payload
+         * encryption. The origin must use HTTPS URI.
+         *
+         * <p>The origin will only contain the scheme, hostname and port of the URL. If the origin
+         * is not provided or is null, PPAPI will use the default coordinator URI.
+         *
+         * <p>The origin must belong to a list of pre-approved coordinator origins. Otherwise,
+         * {@link AdSelectionManager#getAdSelectionData} will throw an IllegalArgumentException
+         */
+        @NonNull
+        @FlaggedApi(Flags.FLAG_FLEDGE_SERVER_AUCTION_MULTI_CLOUD_ENABLED)
+        public GetAdSelectionDataRequest.Builder setCoordinatorOriginUri(
+                @Nullable Uri coordinatorOriginUri) {
+            this.mCoordinatorOriginUri = coordinatorOriginUri;
+            return this;
+        }
+
+        /**
+         * Builds a {@link GetAdSelectionDataRequest} instance.
+         */
+        @NonNull
+        public GetAdSelectionDataRequest build() {
+            return new GetAdSelectionDataRequest(mSeller, mCoordinatorOriginUri);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/GetAdSelectionDataResponse.java b/android-35/android/adservices/adselection/GetAdSelectionDataResponse.java
new file mode 100644
index 0000000..3f95a97
--- /dev/null
+++ b/android-35/android/adservices/adselection/GetAdSelectionDataResponse.java
@@ -0,0 +1,188 @@
+/*
+ * 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 android.adservices.adselection;
+
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID;
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID_MESSAGE;
+
+import android.annotation.Nullable;
+import android.content.res.AssetFileDescriptor;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.adservices.AdServicesParcelableUtil;
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Represents ad selection data collected from device for ad selection.
+ *
+ * @hide
+ */
+public final class GetAdSelectionDataResponse implements Parcelable {
+    private final long mAdSelectionId;
+    @Nullable private final byte[] mAdSelectionData;
+    @Nullable private final AssetFileDescriptor mAssetFileDescriptor;
+
+    public static final Creator<GetAdSelectionDataResponse> CREATOR =
+            new Creator<>() {
+                @Override
+                public GetAdSelectionDataResponse createFromParcel(Parcel in) {
+                    Objects.requireNonNull(in);
+
+                    return new GetAdSelectionDataResponse(in);
+                }
+
+                @Override
+                public GetAdSelectionDataResponse[] newArray(int size) {
+                    return new GetAdSelectionDataResponse[size];
+                }
+            };
+
+    private GetAdSelectionDataResponse(
+            long adSelectionId, byte[] adSelectionData, AssetFileDescriptor assetFileDescriptor) {
+        this.mAdSelectionId = adSelectionId;
+        this.mAdSelectionData = adSelectionData;
+        this.mAssetFileDescriptor = assetFileDescriptor;
+    }
+
+    private GetAdSelectionDataResponse(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        this.mAdSelectionId = in.readLong();
+        this.mAdSelectionData = in.createByteArray();
+        this.mAssetFileDescriptor =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, AssetFileDescriptor.CREATOR::createFromParcel);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof GetAdSelectionDataResponse) {
+            GetAdSelectionDataResponse response = (GetAdSelectionDataResponse) o;
+            return mAdSelectionId == response.mAdSelectionId
+                    && Arrays.equals(mAdSelectionData, response.mAdSelectionData)
+                    && Objects.equals(mAssetFileDescriptor, response.mAssetFileDescriptor);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mAdSelectionId, Arrays.hashCode(mAdSelectionData), mAssetFileDescriptor);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Returns the adSelectionId that identifies the AdSelection. */
+    public long getAdSelectionId() {
+        return mAdSelectionId;
+    }
+
+    /** Returns the adSelectionData that is collected from device. */
+    @Nullable
+    public byte[] getAdSelectionData() {
+        if (Objects.isNull(mAdSelectionData)) {
+            return null;
+        } else {
+            return Arrays.copyOf(mAdSelectionData, mAdSelectionData.length);
+        }
+    }
+
+    /**
+     * Returns the {@link AssetFileDescriptor} that points to a piece of memory where the
+     * adSelectionData is stored
+     */
+    @Nullable
+    public AssetFileDescriptor getAssetFileDescriptor() {
+        return mAssetFileDescriptor;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        dest.writeLong(mAdSelectionId);
+        dest.writeByteArray(mAdSelectionData);
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mAssetFileDescriptor,
+                (targetParcel, sourceSignals) -> sourceSignals.writeToParcel(targetParcel, flags));
+    }
+
+    /**
+     * Builder for {@link GetAdSelectionDataResponse} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private long mAdSelectionId;
+        @Nullable private byte[] mAdSelectionData;
+        @Nullable private AssetFileDescriptor mAssetFileDescriptor;
+
+        public Builder() {}
+
+        /** Sets the adSelectionId. */
+        @NonNull
+        public GetAdSelectionDataResponse.Builder setAdSelectionId(long adSelectionId) {
+            this.mAdSelectionId = adSelectionId;
+            return this;
+        }
+
+        /** Sets the adSelectionData. */
+        @NonNull
+        public GetAdSelectionDataResponse.Builder setAdSelectionData(
+                @Nullable byte[] adSelectionData) {
+            if (!Objects.isNull(adSelectionData)) {
+                this.mAdSelectionData = Arrays.copyOf(adSelectionData, adSelectionData.length);
+            } else {
+                this.mAdSelectionData = null;
+            }
+            return this;
+        }
+
+        /** Sets the assetFileDescriptor */
+        @NonNull
+        public GetAdSelectionDataResponse.Builder setAssetFileDescriptor(
+                @Nullable AssetFileDescriptor assetFileDescriptor) {
+            this.mAssetFileDescriptor = assetFileDescriptor;
+            return this;
+        }
+
+        /**
+         * Builds a {@link GetAdSelectionDataResponse} instance.
+         *
+         * @throws IllegalArgumentException if the adSelectionId is not set
+         */
+        @NonNull
+        public GetAdSelectionDataResponse build() {
+            Preconditions.checkArgument(
+                    mAdSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+
+            return new GetAdSelectionDataResponse(
+                    mAdSelectionId, mAdSelectionData, mAssetFileDescriptor);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/PerBuyerDecisionLogic.java b/android-35/android/adservices/adselection/PerBuyerDecisionLogic.java
new file mode 100644
index 0000000..dd3d705
--- /dev/null
+++ b/android-35/android/adservices/adselection/PerBuyerDecisionLogic.java
@@ -0,0 +1,112 @@
+/*
+ * 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 android.adservices.adselection;
+
+import android.adservices.common.AdTechIdentifier;
+import android.adservices.customaudience.CustomAudience;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.AdServicesParcelableUtil;
+import com.android.adservices.flags.Flags;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * The override object for decision logic JS per buyer for {@link SignedContextualAds}.
+ *
+ * <p>This decision logic is used for reporting when an ad wins from a buyer's bundle of {@link
+ * SignedContextualAds}.
+ *
+ * <p>This JS code may be extended to updating bid values for contextual ads in the future.
+ *
+ * <p>See {@link CustomAudience#getBiddingLogicUri()}.
+ */
+@FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
+public final class PerBuyerDecisionLogic implements Parcelable {
+
+    @NonNull
+    public static final PerBuyerDecisionLogic EMPTY =
+            new PerBuyerDecisionLogic(Collections.emptyMap());
+
+    @NonNull private final Map<AdTechIdentifier, DecisionLogic> mPerBuyerLogicMap;
+
+    /**
+     * Builds a {@link PerBuyerDecisionLogic} instance.
+     *
+     * @param perBuyerLogicMap map of buyers and their decision logic to be fetched during ad
+     *     selection
+     */
+    public PerBuyerDecisionLogic(@NonNull Map<AdTechIdentifier, DecisionLogic> perBuyerLogicMap) {
+        Objects.requireNonNull(perBuyerLogicMap);
+        mPerBuyerLogicMap = perBuyerLogicMap;
+    }
+
+    private PerBuyerDecisionLogic(@NonNull Parcel in) {
+        mPerBuyerLogicMap =
+                AdServicesParcelableUtil.readMapFromParcel(
+                        in, AdTechIdentifier::fromString, DecisionLogic.class);
+    }
+
+    @NonNull
+    public static final Creator<PerBuyerDecisionLogic> CREATOR =
+            new Creator<PerBuyerDecisionLogic>() {
+                @Override
+                public PerBuyerDecisionLogic createFromParcel(Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new PerBuyerDecisionLogic(in);
+                }
+
+                @Override
+                public PerBuyerDecisionLogic[] newArray(int size) {
+                    return new PerBuyerDecisionLogic[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+        AdServicesParcelableUtil.writeMapToParcel(dest, mPerBuyerLogicMap);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPerBuyerLogicMap);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof PerBuyerDecisionLogic)) return false;
+        PerBuyerDecisionLogic logicMap = (PerBuyerDecisionLogic) o;
+        return mPerBuyerLogicMap.equals(logicMap.getPerBuyerLogicMap());
+    }
+
+    @NonNull
+    public Map<AdTechIdentifier, DecisionLogic> getPerBuyerLogicMap() {
+        return mPerBuyerLogicMap;
+    }
+}
diff --git a/android-35/android/adservices/adselection/PersistAdSelectionResultInput.java b/android-35/android/adservices/adselection/PersistAdSelectionResultInput.java
new file mode 100644
index 0000000..de0c459
--- /dev/null
+++ b/android-35/android/adservices/adselection/PersistAdSelectionResultInput.java
@@ -0,0 +1,218 @@
+/*
+ * 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 android.adservices.adselection;
+
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID;
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID_MESSAGE;
+
+import android.adservices.common.AdTechIdentifier;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.AdServicesParcelableUtil;
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Represents input params to the PersistAdSelectionResult API.
+ *
+ * @hide
+ */
+public final class PersistAdSelectionResultInput implements Parcelable {
+    private final long mAdSelectionId;
+    @Nullable private final AdTechIdentifier mSeller;
+    @Nullable private final byte[] mAdSelectionResult;
+    @NonNull private final String mCallerPackageName;
+
+    @NonNull
+    public static final Creator<PersistAdSelectionResultInput> CREATOR =
+            new Creator<>() {
+                public PersistAdSelectionResultInput createFromParcel(Parcel in) {
+                    return new PersistAdSelectionResultInput(in);
+                }
+
+                public PersistAdSelectionResultInput[] newArray(int size) {
+                    return new PersistAdSelectionResultInput[size];
+                }
+            };
+
+    private PersistAdSelectionResultInput(
+            long adSelectionId,
+            @Nullable AdTechIdentifier seller,
+            @Nullable byte[] adSelectionResult,
+            @NonNull String callerPackageName) {
+        Objects.requireNonNull(callerPackageName);
+
+        this.mAdSelectionId = adSelectionId;
+        this.mSeller = seller;
+        this.mAdSelectionResult = adSelectionResult;
+        this.mCallerPackageName = callerPackageName;
+    }
+
+    private PersistAdSelectionResultInput(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        this.mAdSelectionId = in.readLong();
+        this.mSeller =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, AdTechIdentifier.CREATOR::createFromParcel);
+        this.mAdSelectionResult = in.createByteArray();
+        this.mCallerPackageName = in.readString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof PersistAdSelectionResultInput) {
+            PersistAdSelectionResultInput obj = (PersistAdSelectionResultInput) o;
+            return mAdSelectionId == obj.mAdSelectionId
+                    && Objects.equals(mSeller, obj.mSeller)
+                    && Arrays.equals(mAdSelectionResult, obj.mAdSelectionResult)
+                    && Objects.equals(mCallerPackageName, obj.mCallerPackageName);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mAdSelectionId, mSeller, Arrays.hashCode(mAdSelectionResult), mCallerPackageName);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        dest.writeLong(mAdSelectionId);
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mSeller,
+                (targetParcel, sourceSignals) -> sourceSignals.writeToParcel(targetParcel, flags));
+        dest.writeByteArray(mAdSelectionResult);
+        dest.writeString(mCallerPackageName);
+    }
+
+    /**
+     * @return an ad selection id.
+     */
+    public long getAdSelectionId() {
+        return mAdSelectionId;
+    }
+
+    /**
+     * @return a seller.
+     */
+    @Nullable
+    public AdTechIdentifier getSeller() {
+        return mSeller;
+    }
+
+    /**
+     * @return an ad selection result.
+     */
+    @Nullable
+    public byte[] getAdSelectionResult() {
+        if (Objects.isNull(mAdSelectionResult)) {
+            return null;
+        } else {
+            return Arrays.copyOf(mAdSelectionResult, mAdSelectionResult.length);
+        }
+    }
+
+    /**
+     * @return the caller package name
+     */
+    @NonNull
+    public String getCallerPackageName() {
+        return mCallerPackageName;
+    }
+
+    /**
+     * Builder for {@link PersistAdSelectionResultInput} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private long mAdSelectionId;
+        @Nullable private AdTechIdentifier mSeller;
+        @Nullable private byte[] mAdSelectionResult;
+        @Nullable private String mCallerPackageName;
+
+        public Builder() {}
+
+        /** Sets the ad selection id {@link Long}. */
+        @NonNull
+        public PersistAdSelectionResultInput.Builder setAdSelectionId(long adSelectionId) {
+            this.mAdSelectionId = adSelectionId;
+            return this;
+        }
+
+        /** Sets the seller {@link AdTechIdentifier}. */
+        @NonNull
+        public PersistAdSelectionResultInput.Builder setSeller(@Nullable AdTechIdentifier seller) {
+            this.mSeller = seller;
+            return this;
+        }
+
+        /** Sets the ad selection result {@link String}. */
+        @NonNull
+        public PersistAdSelectionResultInput.Builder setAdSelectionResult(
+                @Nullable byte[] adSelectionResult) {
+            if (!Objects.isNull(adSelectionResult)) {
+                this.mAdSelectionResult =
+                        Arrays.copyOf(adSelectionResult, adSelectionResult.length);
+            } else {
+                this.mAdSelectionResult = null;
+            }
+            return this;
+        }
+
+        /** Sets the caller's package name. */
+        @NonNull
+        public PersistAdSelectionResultInput.Builder setCallerPackageName(
+                @NonNull String callerPackageName) {
+            Objects.requireNonNull(callerPackageName);
+
+            this.mCallerPackageName = callerPackageName;
+            return this;
+        }
+
+        /**
+         * Builds a {@link PersistAdSelectionResultInput} instance.
+         *
+         * @throws IllegalArgumentException if the adSelectionId is not set
+         * @throws NullPointerException if the CallerPackageName is null
+         */
+        @NonNull
+        public PersistAdSelectionResultInput build() {
+            Objects.requireNonNull(mCallerPackageName);
+            Preconditions.checkArgument(
+                    mAdSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+
+            return new PersistAdSelectionResultInput(
+                    mAdSelectionId, mSeller, mAdSelectionResult, mCallerPackageName);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/PersistAdSelectionResultRequest.java b/android-35/android/adservices/adselection/PersistAdSelectionResultRequest.java
new file mode 100644
index 0000000..b74b756
--- /dev/null
+++ b/android-35/android/adservices/adselection/PersistAdSelectionResultRequest.java
@@ -0,0 +1,157 @@
+/*
+ * 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 android.adservices.adselection;
+
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID;
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID_MESSAGE;
+
+import android.adservices.common.AdTechIdentifier;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.OutcomeReceiver;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Represents a request containing the seller, the ad selection data id and data.
+ *
+ * <p>Instances of this class are created by SDKs to be provided as arguments to the {@link
+ * AdSelectionManager#persistAdSelectionResult} methods in {@link AdSelectionManager}.
+ */
+public final class PersistAdSelectionResultRequest {
+    private final long mAdSelectionId;
+    @Nullable private final AdTechIdentifier mSeller;
+    @Nullable private final byte[] mAdSelectionResult;
+
+    private PersistAdSelectionResultRequest(
+            long adSelectionId,
+            @Nullable AdTechIdentifier seller,
+            @Nullable byte[] adSelectionResult) {
+        this.mAdSelectionId = adSelectionId;
+        this.mSeller = seller;
+        this.mAdSelectionResult = adSelectionResult;
+    }
+
+    /**
+     * @return an ad selection id.
+     * @deprecated Use the {@link #getAdSelectionDataId()} instead, the underlying value is enforced
+     *     to be the same.
+     */
+    public long getAdSelectionId() {
+        return mAdSelectionId;
+    }
+
+    /**
+     * Returns the id that identifies the {@link
+     * AdSelectionManager#getAdSelectionData(GetAdSelectionDataRequest, Executor, OutcomeReceiver)}
+     * payload that generated this result.
+     */
+    @FlaggedApi(
+            "com.android.adservices.flags.fledge_auction_server_get_ad_selection_data_id_enabled")
+    public long getAdSelectionDataId() {
+        return mAdSelectionId;
+    }
+
+    /**
+     * @return a seller.
+     */
+    @Nullable
+    public AdTechIdentifier getSeller() {
+        return mSeller;
+    }
+
+    /**
+     * @return an ad selection result.
+     */
+    @Nullable
+    public byte[] getAdSelectionResult() {
+        if (Objects.isNull(mAdSelectionResult)) {
+            return null;
+        } else {
+            return Arrays.copyOf(mAdSelectionResult, mAdSelectionResult.length);
+        }
+    }
+
+    /** Builder for {@link PersistAdSelectionResultRequest} objects. */
+    public static final class Builder {
+        private long mAdSelectionId;
+        @Nullable private AdTechIdentifier mSeller;
+        @Nullable private byte[] mAdSelectionResult;
+
+        public Builder() {}
+
+        /**
+         * Sets the ad selection id {@link Long}.
+         *
+         * @deprecated Use the {@link #setAdSelectionDataId(long)} instead.
+         */
+        @NonNull
+        public PersistAdSelectionResultRequest.Builder setAdSelectionId(long adSelectionId) {
+            this.mAdSelectionId = adSelectionId;
+            return this;
+        }
+
+        /** Sets the ad selection data id {@link Long}. */
+        @NonNull
+        @FlaggedApi(
+                "com.android.adservices.flags.fledge_auction_server_get_ad_selection_data_id_enabled")
+        public PersistAdSelectionResultRequest.Builder setAdSelectionDataId(
+                long adSelectionDataId) {
+            this.mAdSelectionId = adSelectionDataId;
+            return this;
+        }
+
+        /** Sets the seller {@link AdTechIdentifier}. */
+        @NonNull
+        public PersistAdSelectionResultRequest.Builder setSeller(
+                @Nullable AdTechIdentifier seller) {
+            this.mSeller = seller;
+            return this;
+        }
+
+        /** Sets the ad selection result {@link String}. */
+        @NonNull
+        public PersistAdSelectionResultRequest.Builder setAdSelectionResult(
+                @Nullable byte[] adSelectionResult) {
+            if (!Objects.isNull(adSelectionResult)) {
+                this.mAdSelectionResult =
+                        Arrays.copyOf(adSelectionResult, adSelectionResult.length);
+            } else {
+                this.mAdSelectionResult = null;
+            }
+            return this;
+        }
+
+        /**
+         * Builds a {@link PersistAdSelectionResultRequest} instance.
+         *
+         * @throws IllegalArgumentException if the adSelectionIid is not set
+         */
+        @NonNull
+        public PersistAdSelectionResultRequest build() {
+            Preconditions.checkArgument(
+                    mAdSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+
+            return new PersistAdSelectionResultRequest(mAdSelectionId, mSeller, mAdSelectionResult);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/PersistAdSelectionResultResponse.java b/android-35/android/adservices/adselection/PersistAdSelectionResultResponse.java
new file mode 100644
index 0000000..98e030a
--- /dev/null
+++ b/android-35/android/adservices/adselection/PersistAdSelectionResultResponse.java
@@ -0,0 +1,151 @@
+/*
+ * 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 android.adservices.adselection;
+
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID;
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID_MESSAGE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Represents the response from persistAdSelectionResult.
+ *
+ * @hide
+ */
+public final class PersistAdSelectionResultResponse implements Parcelable {
+    private final long mAdSelectionId;
+    @NonNull private final Uri mAdRenderUri;
+
+    public static final Creator<PersistAdSelectionResultResponse> CREATOR =
+            new Creator<>() {
+                @Override
+                public PersistAdSelectionResultResponse createFromParcel(Parcel in) {
+                    Objects.requireNonNull(in);
+
+                    return new PersistAdSelectionResultResponse(in);
+                }
+
+                @Override
+                public PersistAdSelectionResultResponse[] newArray(int size) {
+                    return new PersistAdSelectionResultResponse[size];
+                }
+            };
+
+    private PersistAdSelectionResultResponse(long adSelectionId, @NonNull Uri adRenderUri) {
+        Objects.requireNonNull(adRenderUri);
+
+        this.mAdSelectionId = adSelectionId;
+        this.mAdRenderUri = adRenderUri;
+    }
+
+    private PersistAdSelectionResultResponse(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        this.mAdSelectionId = in.readLong();
+        this.mAdRenderUri = Uri.CREATOR.createFromParcel(in);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof PersistAdSelectionResultResponse) {
+            PersistAdSelectionResultResponse response = (PersistAdSelectionResultResponse) o;
+            return mAdSelectionId == response.mAdSelectionId
+                    && Objects.equals(mAdRenderUri, response.mAdRenderUri);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAdSelectionId, mAdRenderUri);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Returns the adSelectionId that identifies the AdSelection. */
+    public long getAdSelectionId() {
+        return mAdSelectionId;
+    }
+
+    /** Returns the adSelectionData that is collected from device. */
+    public Uri getAdRenderUri() {
+        return mAdRenderUri;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+        Objects.requireNonNull(mAdRenderUri);
+
+        dest.writeLong(mAdSelectionId);
+        mAdRenderUri.writeToParcel(dest, flags);
+    }
+
+    /**
+     * Builder for {@link PersistAdSelectionResultResponse} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private long mAdSelectionId;
+        @Nullable private Uri mAdRenderUri;
+
+        public Builder() {}
+
+        /** Sets the adSelectionId. */
+        @NonNull
+        public PersistAdSelectionResultResponse.Builder setAdSelectionId(long adSelectionId) {
+            this.mAdSelectionId = adSelectionId;
+            return this;
+        }
+
+        /** Sets the adRenderUri. */
+        @NonNull
+        public PersistAdSelectionResultResponse.Builder setAdRenderUri(@NonNull Uri adRenderUri) {
+            Objects.requireNonNull(adRenderUri);
+
+            this.mAdRenderUri = adRenderUri;
+            return this;
+        }
+
+        /**
+         * Builds a {@link PersistAdSelectionResultResponse} instance.
+         *
+         * @throws IllegalArgumentException if the adSelectionId is not set
+         * @throws NullPointerException if the RenderUri is null
+         */
+        @NonNull
+        public PersistAdSelectionResultResponse build() {
+            Objects.requireNonNull(mAdRenderUri);
+            Preconditions.checkArgument(
+                    mAdSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+
+            return new PersistAdSelectionResultResponse(mAdSelectionId, mAdRenderUri);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/RemoveAdCounterHistogramOverrideInput.java b/android-35/android/adservices/adselection/RemoveAdCounterHistogramOverrideInput.java
new file mode 100644
index 0000000..dfb2570
--- /dev/null
+++ b/android-35/android/adservices/adselection/RemoveAdCounterHistogramOverrideInput.java
@@ -0,0 +1,205 @@
+/*
+ * 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 android.adservices.adselection;
+
+import static android.adservices.adselection.SetAdCounterHistogramOverrideRequest.NULL_BUYER_MESSAGE;
+import static android.adservices.adselection.UpdateAdCounterHistogramRequest.UNSET_AD_EVENT_TYPE_MESSAGE;
+import static android.adservices.common.FrequencyCapFilters.AD_EVENT_TYPE_INVALID;
+
+import android.adservices.common.AdTechIdentifier;
+import android.adservices.common.FrequencyCapFilters;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Input object for removing ad counter histogram overrides.
+ *
+ * <p>Histogram overrides replace actual ad counter histograms used in ad selection. Overrides may
+ * only be set in debuggable apps on phones running a debuggable OS build with developer options
+ * enabled. Overrides are only available from the calling app.
+ *
+ * @hide
+ */
+public final class RemoveAdCounterHistogramOverrideInput implements Parcelable {
+    @FrequencyCapFilters.AdEventType private final int mAdEventType;
+    private final int mAdCounterKey;
+    @NonNull private final AdTechIdentifier mBuyer;
+
+    @NonNull
+    public static final Creator<RemoveAdCounterHistogramOverrideInput> CREATOR =
+            new Creator<RemoveAdCounterHistogramOverrideInput>() {
+                @Override
+                public RemoveAdCounterHistogramOverrideInput createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+
+                    return new RemoveAdCounterHistogramOverrideInput(in);
+                }
+
+                @Override
+                public RemoveAdCounterHistogramOverrideInput[] newArray(int size) {
+                    return new RemoveAdCounterHistogramOverrideInput[size];
+                }
+            };
+
+    private RemoveAdCounterHistogramOverrideInput(@NonNull Builder builder) {
+        Objects.requireNonNull(builder);
+
+        mAdEventType = builder.mAdEventType;
+        mAdCounterKey = builder.mAdCounterKey;
+        mBuyer = builder.mBuyer;
+    }
+
+    private RemoveAdCounterHistogramOverrideInput(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        mAdEventType = in.readInt();
+        mAdCounterKey = in.readInt();
+        mBuyer = AdTechIdentifier.fromString(in.readString());
+    }
+
+    /**
+     * Gets the {@link FrequencyCapFilters.AdEventType} for the ad counter histogram override.
+     *
+     * <p>The ad event type is used with the ad counter key from {@link #getAdCounterKey()} and the
+     * buyer adtech from {@link #getBuyer()} to specify which histogram to use in ad selection
+     * filtering. The ad event type would normally be specified by an app/SDK after a
+     * FLEDGE-selected ad is rendered.
+     */
+    @FrequencyCapFilters.AdEventType
+    public int getAdEventType() {
+        return mAdEventType;
+    }
+
+    /**
+     * Gets the ad counter key for the ad counter histogram override.
+     *
+     * <p>The ad counter key is used with the ad event type from {@link #getAdEventType()} and the
+     * buyer adtech from {@link #getBuyer()} to specify which histogram to use in ad selection
+     * filtering. The ad counter key would normally be specified by a custom audience ad to
+     * represent a grouping to filter on.
+     */
+    @NonNull
+    public int getAdCounterKey() {
+        return mAdCounterKey;
+    }
+
+    /**
+     * Gets the {@link AdTechIdentifier} for the buyer which owns the ad counter histogram.
+     *
+     * <p>During filtering in FLEDGE ad selection, ads can only use ad counter histogram data
+     * generated by the same buyer. For {@link FrequencyCapFilters#AD_EVENT_TYPE_WIN}, ad counter
+     * histogram data is further restricted to ads from the same custom audience, which is
+     * identified by the buyer, the custom audience's owner app package name, and the custom
+     * audience name.
+     */
+    @NonNull
+    public AdTechIdentifier getBuyer() {
+        return mBuyer;
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "RemoveAdCounterHistogramOverrideInput{"
+                + "mAdEventType="
+                + mAdEventType
+                + ", mAdCounterKey="
+                + mAdCounterKey
+                + ", mBuyer="
+                + mBuyer
+                + '}';
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        dest.writeInt(mAdEventType);
+        dest.writeInt(mAdCounterKey);
+        dest.writeString(mBuyer.toString());
+    }
+
+    /** Builder for {@link RemoveAdCounterHistogramOverrideInput} objects. */
+    public static final class Builder {
+        @FrequencyCapFilters.AdEventType private int mAdEventType = AD_EVENT_TYPE_INVALID;
+        private int mAdCounterKey;
+        @Nullable private AdTechIdentifier mBuyer;
+
+        public Builder() {}
+
+        /**
+         * Sets the {@link FrequencyCapFilters.AdEventType} for the ad counter histogram override.
+         *
+         * <p>See {@link #getAdEventType()} for more information.
+         */
+        @NonNull
+        public Builder setAdEventType(@FrequencyCapFilters.AdEventType int adEventType) {
+            mAdEventType = adEventType;
+            return this;
+        }
+
+        /**
+         * Sets the ad counter key for the ad counter histogram override.
+         *
+         * <p>See {@link #getAdCounterKey()} for more information.
+         */
+        @NonNull
+        public Builder setAdCounterKey(int adCounterKey) {
+            mAdCounterKey = adCounterKey;
+            return this;
+        }
+
+        /**
+         * Sets the {@link AdTechIdentifier} for the buyer which owns the ad counter histogram.
+         *
+         * <p>See {@link #getBuyer()} for more information.
+         */
+        @NonNull
+        public Builder setBuyer(@NonNull AdTechIdentifier buyer) {
+            Objects.requireNonNull(buyer, NULL_BUYER_MESSAGE);
+            mBuyer = buyer;
+            return this;
+        }
+
+        /**
+         * Builds the {@link RemoveAdCounterHistogramOverrideInput} object.
+         *
+         * @throws NullPointerException if any parameters are not set
+         * @throws IllegalArgumentException if the ad event type is invalid
+         */
+        @NonNull
+        public RemoveAdCounterHistogramOverrideInput build()
+                throws NullPointerException, IllegalArgumentException {
+            Preconditions.checkArgument(
+                    mAdEventType != AD_EVENT_TYPE_INVALID, UNSET_AD_EVENT_TYPE_MESSAGE);
+            Objects.requireNonNull(mBuyer, NULL_BUYER_MESSAGE);
+
+            return new RemoveAdCounterHistogramOverrideInput(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/RemoveAdCounterHistogramOverrideRequest.java b/android-35/android/adservices/adselection/RemoveAdCounterHistogramOverrideRequest.java
new file mode 100644
index 0000000..ab2e301
--- /dev/null
+++ b/android-35/android/adservices/adselection/RemoveAdCounterHistogramOverrideRequest.java
@@ -0,0 +1,165 @@
+/*
+ * 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 android.adservices.adselection;
+
+import static android.adservices.adselection.SetAdCounterHistogramOverrideRequest.NULL_BUYER_MESSAGE;
+import static android.adservices.adselection.UpdateAdCounterHistogramRequest.UNSET_AD_EVENT_TYPE_MESSAGE;
+import static android.adservices.common.FrequencyCapFilters.AD_EVENT_TYPE_INVALID;
+
+import android.adservices.common.AdTechIdentifier;
+import android.adservices.common.FrequencyCapFilters;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Request object for removing ad counter histogram overrides.
+ *
+ * <p>Histogram overrides replace actual ad counter histograms used in ad selection. Overrides may
+ * only be set in debuggable apps on phones running a debuggable OS build with developer options
+ * enabled. Overrides are only available from the calling app.
+ *
+ * @hide
+ */
+// TODO(b/265204820): Unhide for frequency cap dev override API review
+public class RemoveAdCounterHistogramOverrideRequest {
+    @FrequencyCapFilters.AdEventType private final int mAdEventType;
+    private final int mAdCounterKey;
+    @NonNull private final AdTechIdentifier mBuyer;
+
+    private RemoveAdCounterHistogramOverrideRequest(@NonNull Builder builder) {
+        Objects.requireNonNull(builder);
+
+        mAdEventType = builder.mAdEventType;
+        mAdCounterKey = builder.mAdCounterKey;
+        mBuyer = builder.mBuyer;
+    }
+
+    /**
+     * Gets the {@link FrequencyCapFilters.AdEventType} for the ad counter histogram override.
+     *
+     * <p>The ad event type is used with the ad counter key from {@link #getAdCounterKey()} and the
+     * buyer adtech from {@link #getBuyer()} to specify which histogram to use in ad selection
+     * filtering. The ad event type would normally be specified by an app/SDK after a
+     * FLEDGE-selected ad is rendered.
+     */
+    @FrequencyCapFilters.AdEventType
+    public int getAdEventType() {
+        return mAdEventType;
+    }
+
+    /**
+     * Gets the ad counter key for the ad counter histogram override.
+     *
+     * <p>The ad counter key is used with the ad event type from {@link #getAdEventType()} and the
+     * buyer adtech from {@link #getBuyer()} to specify which histogram to use in ad selection
+     * filtering. The ad counter key would normally be specified by a custom audience ad to
+     * represent a grouping to filter on.
+     */
+    @NonNull
+    public int getAdCounterKey() {
+        return mAdCounterKey;
+    }
+
+    /**
+     * Gets the {@link AdTechIdentifier} for the buyer which owns the ad counter histogram.
+     *
+     * <p>During filtering in FLEDGE ad selection, ads can only use ad counter histogram data
+     * generated by the same buyer. For {@link FrequencyCapFilters#AD_EVENT_TYPE_WIN}, ad counter
+     * histogram data is further restricted to ads from the same custom audience, which is
+     * identified by the buyer, the custom audience's owner app package name, and the custom
+     * audience name.
+     */
+    @NonNull
+    public AdTechIdentifier getBuyer() {
+        return mBuyer;
+    }
+
+    @Override
+    public String toString() {
+        return "RemoveAdCounterHistogramOverrideRequest{"
+                + "mAdEventType="
+                + mAdEventType
+                + ", mAdCounterKey="
+                + mAdCounterKey
+                + ", mBuyer="
+                + mBuyer
+                + '}';
+    }
+
+    /** Builder for {@link RemoveAdCounterHistogramOverrideRequest} objects. */
+    public static final class Builder {
+        @FrequencyCapFilters.AdEventType private int mAdEventType = AD_EVENT_TYPE_INVALID;
+        private int mAdCounterKey;
+        @Nullable private AdTechIdentifier mBuyer;
+
+        public Builder() {}
+
+        /**
+         * Sets the {@link FrequencyCapFilters.AdEventType} for the ad counter histogram override.
+         *
+         * <p>See {@link #getAdEventType()} for more information.
+         */
+        @NonNull
+        public Builder setAdEventType(@FrequencyCapFilters.AdEventType int adEventType) {
+            mAdEventType = adEventType;
+            return this;
+        }
+
+        /**
+         * Sets the ad counter key for the ad counter histogram override.
+         *
+         * <p>See {@link #getAdCounterKey()} for more information.
+         */
+        @NonNull
+        public Builder setAdCounterKey(int adCounterKey) {
+            mAdCounterKey = adCounterKey;
+            return this;
+        }
+
+        /**
+         * Sets the {@link AdTechIdentifier} for the buyer which owns the ad counter histogram.
+         *
+         * <p>See {@link #getBuyer()} for more information.
+         */
+        @NonNull
+        public Builder setBuyer(@NonNull AdTechIdentifier buyer) {
+            Objects.requireNonNull(buyer, NULL_BUYER_MESSAGE);
+            mBuyer = buyer;
+            return this;
+        }
+
+        /**
+         * Builds the {@link RemoveAdCounterHistogramOverrideRequest} object.
+         *
+         * @throws NullPointerException if any parameters are not set
+         * @throws IllegalArgumentException if the ad event type is invalid
+         */
+        @NonNull
+        public RemoveAdCounterHistogramOverrideRequest build()
+                throws NullPointerException, IllegalArgumentException {
+            Preconditions.checkArgument(
+                    mAdEventType != AD_EVENT_TYPE_INVALID, UNSET_AD_EVENT_TYPE_MESSAGE);
+            Objects.requireNonNull(mBuyer, NULL_BUYER_MESSAGE);
+
+            return new RemoveAdCounterHistogramOverrideRequest(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/RemoveAdSelectionFromOutcomesOverrideRequest.java b/android-35/android/adservices/adselection/RemoveAdSelectionFromOutcomesOverrideRequest.java
new file mode 100644
index 0000000..8775b54
--- /dev/null
+++ b/android-35/android/adservices/adselection/RemoveAdSelectionFromOutcomesOverrideRequest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import android.annotation.NonNull;
+
+/**
+ * This POJO represents the {@link TestAdSelectionManager
+ * #removeAdSelectionFromOutcomesConfigRemoteInfoOverride(
+ * RemoveAdSelectionFromOutcomesOverrideRequest, Executor, OutcomeReceiver)} request
+ *
+ * <p>It contains one field, a {@link AdSelectionFromOutcomesConfig} which serves as the identifier
+ * of the override to be removed
+ *
+ */
+public class RemoveAdSelectionFromOutcomesOverrideRequest {
+    @NonNull private final AdSelectionFromOutcomesConfig mAdSelectionFromOutcomesConfig;
+
+    /** Builds a {@link RemoveAdSelectionOverrideRequest} instance. */
+    public RemoveAdSelectionFromOutcomesOverrideRequest(
+            @NonNull AdSelectionFromOutcomesConfig config) {
+        mAdSelectionFromOutcomesConfig = config;
+    }
+
+    /** @return AdSelectionConfig, the configuration of the ad selection process. */
+    @NonNull
+    public AdSelectionFromOutcomesConfig getAdSelectionFromOutcomesConfig() {
+        return mAdSelectionFromOutcomesConfig;
+    }
+}
diff --git a/android-35/android/adservices/adselection/RemoveAdSelectionOverrideRequest.java b/android-35/android/adservices/adselection/RemoveAdSelectionOverrideRequest.java
new file mode 100644
index 0000000..79e8792
--- /dev/null
+++ b/android-35/android/adservices/adselection/RemoveAdSelectionOverrideRequest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import android.annotation.NonNull;
+import android.os.OutcomeReceiver;
+
+import java.util.concurrent.Executor;
+
+/**
+ * This POJO represents the {@link TestAdSelectionManager#removeAdSelectionConfigRemoteInfoOverride(
+ * RemoveAdSelectionOverrideRequest, Executor, OutcomeReceiver)} request
+ *
+ * <p>It contains one field, a {@link AdSelectionConfig} which serves as the identifier of the
+ * override to be removed
+ */
+public class RemoveAdSelectionOverrideRequest {
+    @NonNull private final AdSelectionConfig mAdSelectionConfig;
+
+    /** Builds a {@link RemoveAdSelectionOverrideRequest} instance. */
+    public RemoveAdSelectionOverrideRequest(@NonNull AdSelectionConfig adSelectionConfig) {
+        mAdSelectionConfig = adSelectionConfig;
+    }
+
+    /**
+     * @return AdSelectionConfig, the configuration of the ad selection process.
+     */
+    @NonNull
+    public AdSelectionConfig getAdSelectionConfig() {
+        return mAdSelectionConfig;
+    }
+}
diff --git a/android-35/android/adservices/adselection/ReportEventRequest.java b/android-35/android/adservices/adselection/ReportEventRequest.java
new file mode 100644
index 0000000..6581ab1
--- /dev/null
+++ b/android-35/android/adservices/adselection/ReportEventRequest.java
@@ -0,0 +1,269 @@
+/*
+ * 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 android.adservices.adselection;
+
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID;
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID_MESSAGE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.InputEvent;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.StandardCharsets;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Request object wrapping the required arguments needed to report an ad event.
+ */
+public class ReportEventRequest {
+    public static final int FLAG_REPORTING_DESTINATION_SELLER = 1 << 0;
+    public static final int FLAG_REPORTING_DESTINATION_BUYER = 1 << 1;
+    private static final int UNSET_REPORTING_DESTINATIONS = 0;
+    private static final String UNSET_REPORTING_DESTINATIONS_MESSAGE =
+            "Reporting destinations bitfield not set.";
+    private static final String INVALID_REPORTING_DESTINATIONS_MESSAGE =
+            "Invalid reporting destinations bitfield!";
+
+    /** @hide */
+    public static final long REPORT_EVENT_MAX_INTERACTION_DATA_SIZE_B = 64 * 1024; // 64 KB
+
+    private static final String EVENT_DATA_SIZE_MAX_EXCEEDED =
+            "Event data should not exceed " + REPORT_EVENT_MAX_INTERACTION_DATA_SIZE_B + " bytes";
+
+    private final long mAdSelectionId;
+    @NonNull private final String mEventKey;
+    @Nullable private final InputEvent mInputEvent;
+    @NonNull private final String mEventData;
+    @ReportingDestination private final int mReportingDestinations; // buyer, seller, or both
+
+    private ReportEventRequest(@NonNull Builder builder) {
+        Objects.requireNonNull(builder);
+
+        Preconditions.checkArgument(
+                builder.mAdSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+        Preconditions.checkArgument(
+                builder.mReportingDestinations != UNSET_REPORTING_DESTINATIONS,
+                UNSET_REPORTING_DESTINATIONS_MESSAGE);
+        Preconditions.checkArgument(
+                isValidDestination(builder.mReportingDestinations),
+                INVALID_REPORTING_DESTINATIONS_MESSAGE);
+        Preconditions.checkArgument(
+                builder.mEventData.getBytes(StandardCharsets.UTF_8).length
+                        <= REPORT_EVENT_MAX_INTERACTION_DATA_SIZE_B,
+                EVENT_DATA_SIZE_MAX_EXCEEDED);
+
+        this.mAdSelectionId = builder.mAdSelectionId;
+        this.mEventKey = builder.mEventKey;
+        this.mInputEvent = builder.mInputEvent;
+        this.mEventData = builder.mEventData;
+        this.mReportingDestinations = builder.mReportingDestinations;
+    }
+
+    /**
+     * Returns the adSelectionId, the primary identifier of an ad selection process.
+     */
+    public long getAdSelectionId() {
+        return mAdSelectionId;
+    }
+
+    /**
+     * Returns the event key, the type of ad event to be reported.
+     *
+     * <p>This field will be used to fetch the {@code reportingUri} associated with the {@code
+     * eventKey} registered in {@code registerAdBeacon} after ad selection.
+     *
+     * <p>This field should be an exact match to the {@code eventKey} registered in {@code
+     * registerAdBeacon}. Specific details about {@code registerAdBeacon} can be found at the
+     * documentation of {@link AdSelectionManager#reportImpression}
+     *
+     * <p>The event key (when inspecting its byte array with {@link String#getBytes()}) in {@code
+     * UTF-8} format should not exceed 40 bytes. Any key exceeding this limit will not be registered
+     * during the {@code registerAdBeacon} call.
+     */
+    @NonNull
+    public String getKey() {
+        return mEventKey;
+    }
+
+    /**
+     * Returns the input event associated with the user interaction.
+     *
+     * <p>This field is either {@code null}, representing a <em>view</em> event, or has an {@link
+     * InputEvent} object, representing a <em>click</em> event.
+     */
+    @Nullable
+    public InputEvent getInputEvent() {
+        return mInputEvent;
+    }
+
+    /**
+     * Returns the ad event data.
+     *
+     * <p>After ad selection, this data is generated by the caller. The caller can then call {@link
+     * AdSelectionManager#reportEvent}. This data will be attached in a POST request to the {@code
+     * reportingUri} registered in {@code registerAdBeacon}.
+     *
+     * <p>The size of {@link String#getBytes()} in {@code UTF-8} format should be below 64KB.
+     */
+    @NonNull
+    public String getData() {
+        return mEventData;
+    }
+
+    /**
+     * Returns the bitfield of reporting destinations to report to (buyer, seller, or both).
+     *
+     * <p>To create this bitfield, place an {@code |} bitwise operator between each {@code
+     * reportingDestination} to be reported to. For example to only report to buyer, set the
+     * reportingDestinations field to {@link #FLAG_REPORTING_DESTINATION_BUYER} To only report to
+     * seller, set the reportingDestinations field to {@link #FLAG_REPORTING_DESTINATION_SELLER} To
+     * report to both buyers and sellers, set the reportingDestinations field to {@link
+     * #FLAG_REPORTING_DESTINATION_BUYER} | {@link #FLAG_REPORTING_DESTINATION_SELLER}
+     */
+    @ReportingDestination
+    public int getReportingDestinations() {
+        return mReportingDestinations;
+    }
+
+    /** @hide */
+    @IntDef(
+            flag = true,
+            prefix = {"FLAG_REPORTING_DESTINATION"},
+            value = {FLAG_REPORTING_DESTINATION_SELLER, FLAG_REPORTING_DESTINATION_BUYER})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ReportingDestination {}
+
+    private static boolean isValidDestination(@ReportingDestination int reportingDestinations) {
+        return 0 < reportingDestinations
+                && reportingDestinations
+                        <= (FLAG_REPORTING_DESTINATION_SELLER | FLAG_REPORTING_DESTINATION_BUYER);
+    }
+
+    /** Builder for {@link ReportEventRequest} objects. */
+    public static final class Builder {
+
+        private long mAdSelectionId;
+        @NonNull private String mEventKey;
+        @Nullable private InputEvent mInputEvent;
+        @NonNull private String mEventData;
+        @ReportingDestination private int mReportingDestinations; // buyer, seller, or both
+
+        public Builder(
+                long adSelectionId,
+                @NonNull String eventKey,
+                @NonNull String eventData,
+                @ReportingDestination int reportingDestinations) {
+            Objects.requireNonNull(eventKey);
+            Objects.requireNonNull(eventData);
+
+            Preconditions.checkArgument(
+                    adSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+            Preconditions.checkArgument(
+                    reportingDestinations != UNSET_REPORTING_DESTINATIONS,
+                    UNSET_REPORTING_DESTINATIONS_MESSAGE);
+            Preconditions.checkArgument(
+                    isValidDestination(reportingDestinations),
+                    INVALID_REPORTING_DESTINATIONS_MESSAGE);
+            Preconditions.checkArgument(
+                    eventData.getBytes(StandardCharsets.UTF_8).length
+                            <= REPORT_EVENT_MAX_INTERACTION_DATA_SIZE_B,
+                    EVENT_DATA_SIZE_MAX_EXCEEDED);
+
+            this.mAdSelectionId = adSelectionId;
+            this.mEventKey = eventKey;
+            this.mEventData = eventData;
+            this.mReportingDestinations = reportingDestinations;
+        }
+
+        /**
+         * Sets the ad selection ID with which the rendered ad's events are associated.
+         *
+         * <p>See {@link #getAdSelectionId()} for more information.
+         */
+        @NonNull
+        public Builder setAdSelectionId(long adSelectionId) {
+            Preconditions.checkArgument(
+                    adSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+            mAdSelectionId = adSelectionId;
+            return this;
+        }
+
+        /**
+         * Sets the event key, the type of ad event to be reported.
+         *
+         * <p>See {@link #getKey()} for more information.
+         */
+        @NonNull
+        public Builder setKey(@NonNull String eventKey) {
+            Objects.requireNonNull(eventKey);
+
+            mEventKey = eventKey;
+            return this;
+        }
+
+        /**
+         * Sets the input event associated with the user interaction.
+         *
+         * <p>See {@link #getInputEvent()} for more information.
+         */
+        @NonNull
+        public Builder setInputEvent(@Nullable InputEvent inputEvent) {
+            mInputEvent = inputEvent;
+            return this;
+        }
+
+        /**
+         * Sets the ad event data.
+         *
+         * <p>See {@link #getData()} for more information.
+         */
+        @NonNull
+        public Builder setData(@NonNull String eventData) {
+            Objects.requireNonNull(eventData);
+
+            mEventData = eventData;
+            return this;
+        }
+
+        /**
+         * Sets the bitfield of reporting destinations to report to (buyer, seller, or both).
+         *
+         * <p>See {@link #getReportingDestinations()} for more information.
+         */
+        @NonNull
+        public Builder setReportingDestinations(@ReportingDestination int reportingDestinations) {
+            Preconditions.checkArgument(
+                    isValidDestination(reportingDestinations),
+                    INVALID_REPORTING_DESTINATIONS_MESSAGE);
+
+            mReportingDestinations = reportingDestinations;
+            return this;
+        }
+
+        /** Builds the {@link ReportEventRequest} object. */
+        @NonNull
+        public ReportEventRequest build() {
+            return new ReportEventRequest(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/ReportImpressionInput.java b/android-35/android/adservices/adselection/ReportImpressionInput.java
new file mode 100644
index 0000000..3e09b17
--- /dev/null
+++ b/android-35/android/adservices/adselection/ReportImpressionInput.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID;
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID_MESSAGE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Represent input params to the reportImpression API.
+ *
+ * @hide
+ */
+public final class ReportImpressionInput implements Parcelable {
+    private final long mAdSelectionId;
+    @NonNull private final AdSelectionConfig mAdSelectionConfig;
+    @NonNull private final String mCallerPackageName;
+
+    @NonNull
+    public static final Parcelable.Creator<ReportImpressionInput> CREATOR =
+            new Parcelable.Creator<ReportImpressionInput>() {
+                public ReportImpressionInput createFromParcel(Parcel in) {
+                    return new ReportImpressionInput(in);
+                }
+
+                public ReportImpressionInput[] newArray(int size) {
+                    return new ReportImpressionInput[size];
+                }
+            };
+
+    private ReportImpressionInput(
+            long adSelectionId,
+            @NonNull AdSelectionConfig adSelectionConfig,
+            @NonNull String callerPackageName) {
+        Objects.requireNonNull(adSelectionConfig);
+
+        this.mAdSelectionId = adSelectionId;
+        this.mAdSelectionConfig = adSelectionConfig;
+        this.mCallerPackageName = callerPackageName;
+    }
+
+    private ReportImpressionInput(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        this.mAdSelectionId = in.readLong();
+        this.mAdSelectionConfig = AdSelectionConfig.CREATOR.createFromParcel(in);
+        this.mCallerPackageName = in.readString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        dest.writeLong(mAdSelectionId);
+        mAdSelectionConfig.writeToParcel(dest, flags);
+        dest.writeString(mCallerPackageName);
+    }
+
+    /**
+     * Returns the adSelectionId, one of the inputs to {@link ReportImpressionInput} as noted in
+     * {@code AdSelectionService}.
+     */
+    public long getAdSelectionId() {
+        return mAdSelectionId;
+    }
+
+    /**
+     * Returns the adSelectionConfig, one of the inputs to {@link ReportImpressionInput} as noted in
+     * {@code AdSelectionService}.
+     */
+    @NonNull
+    public AdSelectionConfig getAdSelectionConfig() {
+        return mAdSelectionConfig;
+    }
+
+    /** @return the caller package name */
+    @NonNull
+    public String getCallerPackageName() {
+        return mCallerPackageName;
+    }
+
+    /**
+     * Builder for {@link ReportImpressionInput} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private long mAdSelectionId = UNSET_AD_SELECTION_ID;
+        @Nullable private AdSelectionConfig mAdSelectionConfig;
+        private String mCallerPackageName;
+
+        public Builder() {}
+
+        /** Set the mAdSelectionId. */
+        @NonNull
+        public ReportImpressionInput.Builder setAdSelectionId(long adSelectionId) {
+            this.mAdSelectionId = adSelectionId;
+            return this;
+        }
+
+        /** Set the AdSelectionConfig. */
+        @NonNull
+        public ReportImpressionInput.Builder setAdSelectionConfig(
+                @NonNull AdSelectionConfig adSelectionConfig) {
+            Objects.requireNonNull(adSelectionConfig);
+
+            this.mAdSelectionConfig = adSelectionConfig;
+            return this;
+        }
+
+        /** Sets the caller's package name. */
+        @NonNull
+        public ReportImpressionInput.Builder setCallerPackageName(
+                @NonNull String callerPackageName) {
+            Objects.requireNonNull(callerPackageName);
+
+            this.mCallerPackageName = callerPackageName;
+            return this;
+        }
+
+        /** Builds a {@link ReportImpressionInput} instance. */
+        @NonNull
+        public ReportImpressionInput build() {
+            Objects.requireNonNull(mAdSelectionConfig);
+            Objects.requireNonNull(mCallerPackageName);
+
+            Preconditions.checkArgument(
+                    mAdSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+
+            return new ReportImpressionInput(
+                    mAdSelectionId, mAdSelectionConfig, mCallerPackageName);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/ReportImpressionRequest.java b/android-35/android/adservices/adselection/ReportImpressionRequest.java
new file mode 100644
index 0000000..3d576ff
--- /dev/null
+++ b/android-35/android/adservices/adselection/ReportImpressionRequest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID;
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID_MESSAGE;
+
+import android.annotation.NonNull;
+import android.os.OutcomeReceiver;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Represent input parameters to the reportImpression API.
+ */
+public class ReportImpressionRequest {
+    private final long mAdSelectionId;
+    @NonNull private final AdSelectionConfig mAdSelectionConfig;
+
+    /**
+     * Ctor for on-device ad selection reporting request.
+     *
+     * <p>If your {@code adSelectionId} is for a on-device auction run using {@link
+     * AdSelectionManager#selectAds(AdSelectionConfig, Executor, OutcomeReceiver)} then your
+     * impression reporting request must include your {@link AdSelectionConfig}.
+     *
+     * @param adSelectionId received from {@link AdSelectionManager#selectAds(AdSelectionConfig,
+     *     Executor, OutcomeReceiver)}
+     * @param adSelectionConfig same {@link AdSelectionConfig} used to trigger {@link
+     *     AdSelectionManager#selectAds(AdSelectionConfig, Executor, OutcomeReceiver)}
+     */
+    public ReportImpressionRequest(
+            long adSelectionId, @NonNull AdSelectionConfig adSelectionConfig) {
+        Objects.requireNonNull(adSelectionConfig);
+        Preconditions.checkArgument(
+                adSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+
+        mAdSelectionId = adSelectionId;
+        mAdSelectionConfig = adSelectionConfig;
+    }
+
+    /**
+     * Ctor for auction server ad selection reporting request.
+     *
+     * <p>If your {@code adSelectionId} is for a server auction run where device info collected by
+     * {@link AdSelectionManager#getAdSelectionData} then your impression reporting request should
+     * only include the ad selection id.
+     *
+     * <p>{@link AdSelectionManager#persistAdSelectionResult} must be called with the encrypted
+     * result blob from servers before making impression reporting request.
+     *
+     * @param adSelectionId received from {@link AdSelectionManager#getAdSelectionData}
+     */
+    public ReportImpressionRequest(long adSelectionId) {
+        Preconditions.checkArgument(
+                adSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+
+        mAdSelectionId = adSelectionId;
+        mAdSelectionConfig = AdSelectionConfig.EMPTY;
+    }
+
+    /** Returns the adSelectionId, one of the inputs to {@link ReportImpressionRequest} */
+    public long getAdSelectionId() {
+        return mAdSelectionId;
+    }
+
+    /** Returns the adSelectionConfig, one of the inputs to {@link ReportImpressionRequest} */
+    @NonNull
+    public AdSelectionConfig getAdSelectionConfig() {
+        return mAdSelectionConfig;
+    }
+}
diff --git a/android-35/android/adservices/adselection/ReportInteractionInput.java b/android-35/android/adservices/adselection/ReportInteractionInput.java
new file mode 100644
index 0000000..2c6dae9
--- /dev/null
+++ b/android-35/android/adservices/adselection/ReportInteractionInput.java
@@ -0,0 +1,305 @@
+/*
+ * 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 android.adservices.adselection;
+
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID;
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID_MESSAGE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.InputEvent;
+
+import com.android.adservices.AdServicesParcelableUtil;
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Input object wrapping the required arguments needed to report an interaction.
+ *
+ * @hide
+ */
+public final class ReportInteractionInput implements Parcelable {
+
+    private static final int UNSET_REPORTING_DESTINATIONS = 0;
+    private static final String UNSET_REPORTING_DESTINATIONS_MESSAGE =
+            "Reporting Destinations bitfield not set.";
+
+    private final long mAdSelectionId;
+    @NonNull private final String mInteractionKey;
+    @NonNull private final String mInteractionData;
+    @NonNull private final String mCallerPackageName;
+    private final int mReportingDestinations; // buyer, seller, or both
+    @Nullable private final InputEvent mInputEvent;
+    @Nullable private final String mAdId;
+    @Nullable private final String mCallerSdkName;
+
+    @NonNull
+    public static final Creator<ReportInteractionInput> CREATOR =
+            new Creator<ReportInteractionInput>() {
+                @Override
+                public ReportInteractionInput createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+
+                    return new ReportInteractionInput(in);
+                }
+
+                @Override
+                public ReportInteractionInput[] newArray(int size) {
+                    return new ReportInteractionInput[size];
+                }
+            };
+
+    private ReportInteractionInput(
+            long adSelectionId,
+            @NonNull String interactionKey,
+            @NonNull String interactionData,
+            @NonNull String callerPackageName,
+            int reportingDestinations,
+            @Nullable InputEvent inputEvent,
+            @Nullable String adId,
+            @Nullable String callerSdkName) {
+        Objects.requireNonNull(interactionKey);
+        Objects.requireNonNull(interactionData);
+        Objects.requireNonNull(callerPackageName);
+
+        this.mAdSelectionId = adSelectionId;
+        this.mInteractionKey = interactionKey;
+        this.mInteractionData = interactionData;
+        this.mCallerPackageName = callerPackageName;
+        this.mReportingDestinations = reportingDestinations;
+        this.mInputEvent = inputEvent;
+        this.mAdId = adId;
+        this.mCallerSdkName = callerSdkName;
+    }
+
+    private ReportInteractionInput(@NonNull Parcel in) {
+        this.mAdSelectionId = in.readLong();
+        this.mInteractionKey = in.readString();
+        this.mInteractionData = in.readString();
+        this.mCallerPackageName = in.readString();
+        this.mReportingDestinations = in.readInt();
+        this.mInputEvent =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, InputEvent.CREATOR::createFromParcel);
+        this.mAdId =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, (sourceParcel -> in.readString()));
+        this.mCallerSdkName =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, (sourceParcel -> in.readString()));
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+        dest.writeLong(mAdSelectionId);
+        dest.writeString(mInteractionKey);
+        dest.writeString(mInteractionData);
+        dest.writeString(mCallerPackageName);
+        dest.writeInt(mReportingDestinations);
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mInputEvent,
+                (targetParcel, sourceInputEvent) ->
+                        sourceInputEvent.writeToParcel(targetParcel, flags));
+        AdServicesParcelableUtil.writeNullableToParcel(dest, mAdId, Parcel::writeString);
+        AdServicesParcelableUtil.writeNullableToParcel(dest, mCallerSdkName, Parcel::writeString);
+    }
+
+    /** Returns the adSelectionId, the primary identifier of an ad selection process. */
+    public long getAdSelectionId() {
+        return mAdSelectionId;
+    }
+
+    /**
+     * Returns the interaction key, the type of interaction to be reported.
+     *
+     * <p>This will be used to fetch the {@code interactionReportingUri} associated with the {@code
+     * interactionKey} registered in {@code registerAdBeacon} after ad selection.
+     */
+    @NonNull
+    public String getInteractionKey() {
+        return mInteractionKey;
+    }
+
+    /**
+     * Returns the interaction data.
+     *
+     * <p>After ad selection, this data is generated by the caller, and will be attached in a POST
+     * request to the {@code interactionReportingUri} registered in {@code registerAdBeacon}.
+     */
+    @NonNull
+    public String getInteractionData() {
+        return mInteractionData;
+    }
+
+    /** Returns the caller package name */
+    @NonNull
+    public String getCallerPackageName() {
+        return mCallerPackageName;
+    }
+
+    /** Returns the bitfield of reporting destinations to report to (buyer, seller, or both) */
+    public int getReportingDestinations() {
+        return mReportingDestinations;
+    }
+
+    /**
+     * Returns the input event associated with the user interaction.
+     *
+     * <p>This field is either {@code null}, representing a <em>view</em> event, or has an {@link
+     * InputEvent} object, representing a <em>click</em> event.
+     */
+    @Nullable
+    public InputEvent getInputEvent() {
+        return mInputEvent;
+    }
+
+    /**
+     * Returns the {@code AdId} to enable event-level debug reporting.
+     *
+     * <p>This field is either set and non-{@code null}, representing a valid and enabled {@code
+     * AdId} or {@code null}, representing an invalid or disabled {@code AdId}.
+     */
+    @Nullable
+    public String getAdId() {
+        return mAdId;
+    }
+
+    /** Returns the caller's sdk name. */
+    @Nullable
+    public String getCallerSdkName() {
+        return mCallerSdkName;
+    }
+
+    /**
+     * Builder for {@link ReportInteractionInput} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private long mAdSelectionId = UNSET_AD_SELECTION_ID;
+        @Nullable private String mInteractionKey;
+        @Nullable private String mInteractionData;
+        @Nullable private String mCallerPackageName;
+        @Nullable private String mCallerSdkName;
+        private int mReportingDestinations = UNSET_REPORTING_DESTINATIONS;
+        @Nullable private InputEvent mInputEvent;
+        @Nullable private String mAdId;
+
+        public Builder() {}
+
+        /** Sets the adSelectionId. */
+        @NonNull
+        public ReportInteractionInput.Builder setAdSelectionId(long adSelectionId) {
+            mAdSelectionId = adSelectionId;
+            return this;
+        }
+
+        /** Sets the interactionKey. */
+        @NonNull
+        public ReportInteractionInput.Builder setInteractionKey(@NonNull String interactionKey) {
+            Objects.requireNonNull(interactionKey);
+
+            mInteractionKey = interactionKey;
+            return this;
+        }
+
+        /** Sets the interactionData. */
+        @NonNull
+        public ReportInteractionInput.Builder setInteractionData(@NonNull String interactionData) {
+            Objects.requireNonNull(interactionData);
+
+            mInteractionData = interactionData;
+            return this;
+        }
+
+        /** Sets the caller's package name. */
+        @NonNull
+        public ReportInteractionInput.Builder setCallerPackageName(
+                @NonNull String callerPackageName) {
+            Objects.requireNonNull(callerPackageName);
+
+            mCallerPackageName = callerPackageName;
+            return this;
+        }
+
+        /** Sets the bitfield of reporting destinations. */
+        @NonNull
+        public ReportInteractionInput.Builder setReportingDestinations(int reportingDestinations) {
+            Preconditions.checkArgument(
+                    reportingDestinations != UNSET_REPORTING_DESTINATIONS,
+                    UNSET_REPORTING_DESTINATIONS_MESSAGE);
+
+            mReportingDestinations = reportingDestinations;
+            return this;
+        }
+
+        /** Sets the input event associated with the user interaction. */
+        @NonNull
+        public ReportInteractionInput.Builder setInputEvent(@Nullable InputEvent inputEvent) {
+            mInputEvent = inputEvent;
+            return this;
+        }
+
+        /** Sets the {@code AdId}. */
+        @NonNull
+        public ReportInteractionInput.Builder setAdId(@Nullable String adId) {
+            mAdId = adId;
+            return this;
+        }
+
+        /** Sets the caller's sdk name. */
+        @NonNull
+        public ReportInteractionInput.Builder setCallerSdkName(@Nullable String callerSdkName) {
+            mCallerSdkName = callerSdkName;
+            return this;
+        }
+
+        /** Builds a {@link ReportInteractionInput} instance. */
+        @NonNull
+        public ReportInteractionInput build() {
+            Objects.requireNonNull(mInteractionKey);
+            Objects.requireNonNull(mInteractionData);
+            Objects.requireNonNull(mCallerPackageName);
+
+            Preconditions.checkArgument(
+                    mAdSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+            Preconditions.checkArgument(
+                    mReportingDestinations != UNSET_REPORTING_DESTINATIONS,
+                    UNSET_REPORTING_DESTINATIONS_MESSAGE);
+
+            return new ReportInteractionInput(
+                    mAdSelectionId,
+                    mInteractionKey,
+                    mInteractionData,
+                    mCallerPackageName,
+                    mReportingDestinations,
+                    mInputEvent,
+                    mAdId,
+                    mCallerSdkName);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/SetAdCounterHistogramOverrideInput.java b/android-35/android/adservices/adselection/SetAdCounterHistogramOverrideInput.java
new file mode 100644
index 0000000..8c16f0d
--- /dev/null
+++ b/android-35/android/adservices/adselection/SetAdCounterHistogramOverrideInput.java
@@ -0,0 +1,313 @@
+/*
+ * 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 android.adservices.adselection;
+
+import static android.adservices.adselection.SetAdCounterHistogramOverrideRequest.NULL_BUYER_MESSAGE;
+import static android.adservices.adselection.SetAdCounterHistogramOverrideRequest.NULL_CUSTOM_AUDIENCE_NAME_MESSAGE;
+import static android.adservices.adselection.SetAdCounterHistogramOverrideRequest.NULL_CUSTOM_AUDIENCE_OWNER_MESSAGE;
+import static android.adservices.adselection.SetAdCounterHistogramOverrideRequest.NULL_HISTOGRAM_TIMESTAMPS_MESSAGE;
+import static android.adservices.adselection.UpdateAdCounterHistogramRequest.UNSET_AD_EVENT_TYPE_MESSAGE;
+import static android.adservices.common.FrequencyCapFilters.AD_EVENT_TYPE_INVALID;
+
+import android.adservices.common.AdTechIdentifier;
+import android.adservices.common.FrequencyCapFilters;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.AdServicesParcelableUtil;
+import com.android.internal.util.Preconditions;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Input object for setting ad counter histogram overrides.
+ *
+ * <p>Histogram overrides replace actual ad counter histograms used in ad selection. Overrides may
+ * only be set in debuggable apps on phones running a debuggable OS build with developer options
+ * enabled. Overrides are only available from the calling app.
+ *
+ * @hide
+ */
+public final class SetAdCounterHistogramOverrideInput implements Parcelable {
+    @FrequencyCapFilters.AdEventType private final int mAdEventType;
+    private final int mAdCounterKey;
+    @NonNull private final List<Instant> mHistogramTimestamps;
+    @NonNull private final AdTechIdentifier mBuyer;
+    @NonNull private final String mCustomAudienceOwner;
+    @NonNull private final String mCustomAudienceName;
+
+    @NonNull
+    public static final Creator<SetAdCounterHistogramOverrideInput> CREATOR =
+            new Creator<SetAdCounterHistogramOverrideInput>() {
+                @Override
+                public SetAdCounterHistogramOverrideInput createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+
+                    return new SetAdCounterHistogramOverrideInput(in);
+                }
+
+                @Override
+                public SetAdCounterHistogramOverrideInput[] newArray(int size) {
+                    return new SetAdCounterHistogramOverrideInput[size];
+                }
+            };
+
+    private SetAdCounterHistogramOverrideInput(@NonNull Builder builder) {
+        Objects.requireNonNull(builder);
+
+        mAdEventType = builder.mAdEventType;
+        mAdCounterKey = builder.mAdCounterKey;
+        mHistogramTimestamps = builder.mHistogramTimestamps;
+        mBuyer = builder.mBuyer;
+        mCustomAudienceOwner = builder.mCustomAudienceOwner;
+        mCustomAudienceName = builder.mCustomAudienceName;
+    }
+
+    private SetAdCounterHistogramOverrideInput(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        mAdEventType = in.readInt();
+        mAdCounterKey = in.readInt();
+        mHistogramTimestamps = AdServicesParcelableUtil.readInstantListFromParcel(in);
+        mBuyer = AdTechIdentifier.fromString(in.readString());
+        mCustomAudienceOwner = in.readString();
+        mCustomAudienceName = in.readString();
+    }
+
+    /**
+     * Gets the {@link FrequencyCapFilters.AdEventType} for the ad counter histogram override.
+     *
+     * <p>The ad event type is used with the ad counter key from {@link #getAdCounterKey()} and the
+     * buyer adtech from {@link #getBuyer()} to specify which histogram to use in ad selection
+     * filtering. The ad event type would normally be specified by an app/SDK after a
+     * FLEDGE-selected ad is rendered.
+     */
+    @FrequencyCapFilters.AdEventType
+    public int getAdEventType() {
+        return mAdEventType;
+    }
+
+    /**
+     * Gets the ad counter key for the ad counter histogram override.
+     *
+     * <p>The ad counter key is used with the ad event type from {@link #getAdEventType()} and the
+     * buyer adtech from {@link #getBuyer()} to specify which histogram to use in ad selection
+     * filtering. The ad counter key would normally be specified by a custom audience ad to
+     * represent a grouping to filter on.
+     */
+    @NonNull
+    public int getAdCounterKey() {
+        return mAdCounterKey;
+    }
+
+    /**
+     * Gets the list of {@link Instant} objects for the ad counter histogram override.
+     *
+     * <p>When set, this list of timestamps is used to populate the override histogram, which is
+     * used instead of actual histograms for ad selection filtering.
+     */
+    @NonNull
+    public List<Instant> getHistogramTimestamps() {
+        return mHistogramTimestamps;
+    }
+
+    /**
+     * Gets the {@link AdTechIdentifier} for the buyer which owns the ad counter histogram.
+     *
+     * <p>During filtering in FLEDGE ad selection, ads can only use ad counter histogram data
+     * generated by the same buyer. For {@link FrequencyCapFilters#AD_EVENT_TYPE_WIN}, ad counter
+     * histogram data is further restricted to ads from the same custom audience, which is
+     * identified by the buyer, the custom audience's owner app package name from {@link
+     * #getCustomAudienceOwner()}, and the custom audience name from {@link
+     * #getCustomAudienceName()}.
+     */
+    @NonNull
+    public AdTechIdentifier getBuyer() {
+        return mBuyer;
+    }
+
+    /**
+     * Gets the package name for the app which generated the custom audience which is associated
+     * with the overridden ad counter histogram data.
+     *
+     * <p>For {@link FrequencyCapFilters#AD_EVENT_TYPE_WIN}, ad counter histogram data is restricted
+     * to ads from the same custom audience, which is identified by the buyer from {@link
+     * #getBuyer()}, the custom audience's owner app package name, and the custom audience name from
+     * {@link #getCustomAudienceName()}.
+     */
+    @NonNull
+    public String getCustomAudienceOwner() {
+        return mCustomAudienceOwner;
+    }
+
+    /**
+     * Gets the buyer-generated name for the custom audience which is associated with the overridden
+     * ad counter histogram data.
+     *
+     * <p>For {@link FrequencyCapFilters#AD_EVENT_TYPE_WIN}, ad counter histogram data is restricted
+     * to ads from the same custom audience, which is identified by the buyer from {@link
+     * #getBuyer()}, the custom audience's owner app package name from {@link
+     * #getCustomAudienceOwner()}, and the custom audience name.
+     */
+    @NonNull
+    public String getCustomAudienceName() {
+        return mCustomAudienceName;
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "SetAdCounterHistogramOverrideInput{"
+                + "mAdEventType="
+                + mAdEventType
+                + ", mAdCounterKey="
+                + mAdCounterKey
+                + ", mHistogramTimestamps="
+                + mHistogramTimestamps
+                + ", mBuyer="
+                + mBuyer
+                + ", mCustomAudienceOwner='"
+                + mCustomAudienceOwner
+                + "', mCustomAudienceName='"
+                + mCustomAudienceName
+                + "'}";
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        dest.writeInt(mAdEventType);
+        dest.writeInt(mAdCounterKey);
+        AdServicesParcelableUtil.writeInstantListToParcel(dest, mHistogramTimestamps);
+        dest.writeString(mBuyer.toString());
+        dest.writeString(mCustomAudienceOwner);
+        dest.writeString(mCustomAudienceName);
+    }
+
+    /** Builder for {@link SetAdCounterHistogramOverrideInput} objects. */
+    public static final class Builder {
+        @FrequencyCapFilters.AdEventType private int mAdEventType = AD_EVENT_TYPE_INVALID;
+        private int mAdCounterKey;
+        @NonNull private List<Instant> mHistogramTimestamps = new ArrayList<>();
+        @Nullable private AdTechIdentifier mBuyer;
+        @Nullable private String mCustomAudienceOwner;
+        @Nullable private String mCustomAudienceName;
+
+        public Builder() {}
+
+        /**
+         * Sets the {@link FrequencyCapFilters.AdEventType} for the ad counter histogram override.
+         *
+         * <p>See {@link #getAdEventType()} for more information.
+         */
+        @NonNull
+        public Builder setAdEventType(@FrequencyCapFilters.AdEventType int adEventType) {
+            mAdEventType = adEventType;
+            return this;
+        }
+
+        /**
+         * Sets the ad counter key for the ad counter histogram override.
+         *
+         * <p>See {@link #getAdCounterKey()} for more information.
+         */
+        @NonNull
+        public Builder setAdCounterKey(int adCounterKey) {
+            mAdCounterKey = adCounterKey;
+            return this;
+        }
+
+        /**
+         * Sets the list of {@link Instant} objects for the ad counter histogram override.
+         *
+         * <p>See {@link #getHistogramTimestamps()} for more information.
+         */
+        @NonNull
+        public Builder setHistogramTimestamps(@NonNull List<Instant> histogramTimestamps) {
+            Objects.requireNonNull(histogramTimestamps, NULL_HISTOGRAM_TIMESTAMPS_MESSAGE);
+            mHistogramTimestamps = histogramTimestamps;
+            return this;
+        }
+
+        /**
+         * Sets the {@link AdTechIdentifier} for the buyer which owns the ad counter histogram.
+         *
+         * <p>See {@link #getBuyer()} for more information.
+         */
+        @NonNull
+        public Builder setBuyer(@NonNull AdTechIdentifier buyer) {
+            Objects.requireNonNull(buyer, NULL_BUYER_MESSAGE);
+            mBuyer = buyer;
+            return this;
+        }
+
+        /**
+         * Sets the package name for the app which generated the custom audience which is associated
+         * with the overridden ad counter histogram data.
+         *
+         * <p>See {@link #getCustomAudienceOwner()} for more information.
+         */
+        @NonNull
+        public Builder setCustomAudienceOwner(@NonNull String customAudienceOwner) {
+            Objects.requireNonNull(customAudienceOwner, NULL_CUSTOM_AUDIENCE_OWNER_MESSAGE);
+            mCustomAudienceOwner = customAudienceOwner;
+            return this;
+        }
+
+        /**
+         * Sets the buyer-generated name for the custom audience which is associated with the
+         * overridden ad counter histogram data.
+         *
+         * <p>See {@link #getCustomAudienceName()} for more information.
+         */
+        @NonNull
+        public Builder setCustomAudienceName(@NonNull String customAudienceName) {
+            Objects.requireNonNull(customAudienceName, NULL_CUSTOM_AUDIENCE_NAME_MESSAGE);
+            mCustomAudienceName = customAudienceName;
+            return this;
+        }
+
+        /**
+         * Builds the {@link SetAdCounterHistogramOverrideInput} object.
+         *
+         * @throws NullPointerException if any parameters are not set
+         * @throws IllegalArgumentException if the ad event type is invalid
+         */
+        @NonNull
+        public SetAdCounterHistogramOverrideInput build()
+                throws NullPointerException, IllegalArgumentException {
+            Preconditions.checkArgument(
+                    mAdEventType != AD_EVENT_TYPE_INVALID, UNSET_AD_EVENT_TYPE_MESSAGE);
+            Objects.requireNonNull(mBuyer, NULL_BUYER_MESSAGE);
+            Objects.requireNonNull(mCustomAudienceOwner, NULL_CUSTOM_AUDIENCE_OWNER_MESSAGE);
+            Objects.requireNonNull(mCustomAudienceName, NULL_CUSTOM_AUDIENCE_NAME_MESSAGE);
+
+            return new SetAdCounterHistogramOverrideInput(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/SetAdCounterHistogramOverrideRequest.java b/android-35/android/adservices/adselection/SetAdCounterHistogramOverrideRequest.java
new file mode 100644
index 0000000..e189b6f
--- /dev/null
+++ b/android-35/android/adservices/adselection/SetAdCounterHistogramOverrideRequest.java
@@ -0,0 +1,277 @@
+/*
+ * 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 android.adservices.adselection;
+
+import static android.adservices.adselection.UpdateAdCounterHistogramRequest.UNSET_AD_EVENT_TYPE_MESSAGE;
+import static android.adservices.common.FrequencyCapFilters.AD_EVENT_TYPE_INVALID;
+
+import android.adservices.common.AdTechIdentifier;
+import android.adservices.common.FrequencyCapFilters;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.Preconditions;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Request object for setting ad counter histogram overrides.
+ *
+ * <p>Histogram overrides replace actual ad counter histograms used in ad selection. Overrides may
+ * only be set in debuggable apps on phones running a debuggable OS build with developer options
+ * enabled. Overrides are only available from the calling app.
+ *
+ * @hide
+ */
+// TODO(b/265204820): Unhide for frequency cap dev override API review
+public class SetAdCounterHistogramOverrideRequest {
+    /** @hide */
+    public static final String NULL_HISTOGRAM_TIMESTAMPS_MESSAGE =
+            "List of histogram timestamps must not be null";
+
+    /** @hide */
+    public static final String NULL_BUYER_MESSAGE = "Buyer must not be null";
+
+    /** @hide */
+    public static final String NULL_CUSTOM_AUDIENCE_OWNER_MESSAGE =
+            "Custom audience owner must not be null";
+
+    /** @hide */
+    public static final String NULL_CUSTOM_AUDIENCE_NAME_MESSAGE =
+            "Custom audience name must not be null";
+
+    @FrequencyCapFilters.AdEventType private final int mAdEventType;
+    private final int mAdCounterKey;
+    @NonNull private final List<Instant> mHistogramTimestamps;
+    @NonNull private final AdTechIdentifier mBuyer;
+    @NonNull private final String mCustomAudienceOwner;
+    @NonNull private final String mCustomAudienceName;
+
+    private SetAdCounterHistogramOverrideRequest(@NonNull Builder builder) {
+        Objects.requireNonNull(builder);
+
+        mAdEventType = builder.mAdEventType;
+        mAdCounterKey = builder.mAdCounterKey;
+        mHistogramTimestamps = builder.mHistogramTimestamps;
+        mBuyer = builder.mBuyer;
+        mCustomAudienceOwner = builder.mCustomAudienceOwner;
+        mCustomAudienceName = builder.mCustomAudienceName;
+    }
+
+    /**
+     * Gets the {@link FrequencyCapFilters.AdEventType} for the ad counter histogram override.
+     *
+     * <p>The ad event type is used with the ad counter key from {@link #getAdCounterKey()} and the
+     * buyer adtech from {@link #getBuyer()} to specify which histogram to use in ad selection
+     * filtering. The ad event type would normally be specified by an app/SDK after a
+     * FLEDGE-selected ad is rendered.
+     */
+    @FrequencyCapFilters.AdEventType
+    public int getAdEventType() {
+        return mAdEventType;
+    }
+
+    /**
+     * Gets the ad counter key for the ad counter histogram override.
+     *
+     * <p>The ad counter key is used with the ad event type from {@link #getAdEventType()} and the
+     * buyer adtech from {@link #getBuyer()} to specify which histogram to use in ad selection
+     * filtering. The ad counter key would normally be specified by a custom audience ad to
+     * represent a grouping to filter on.
+     */
+    @NonNull
+    public int getAdCounterKey() {
+        return mAdCounterKey;
+    }
+
+    /**
+     * Gets the list of {@link Instant} objects for the ad counter histogram override.
+     *
+     * <p>When set, this list of timestamps is used to populate the override histogram, which is
+     * used instead of actual histograms for ad selection filtering.
+     */
+    @NonNull
+    public List<Instant> getHistogramTimestamps() {
+        return mHistogramTimestamps;
+    }
+
+    /**
+     * Gets the {@link AdTechIdentifier} for the buyer which owns the ad counter histogram.
+     *
+     * <p>During filtering in FLEDGE ad selection, ads can only use ad counter histogram data
+     * generated by the same buyer. For {@link FrequencyCapFilters#AD_EVENT_TYPE_WIN}, ad counter
+     * histogram data is further restricted to ads from the same custom audience, which is
+     * identified by the buyer, the custom audience's owner app package name from {@link
+     * #getCustomAudienceOwner()}, and the custom audience name from {@link
+     * #getCustomAudienceName()}.
+     */
+    @NonNull
+    public AdTechIdentifier getBuyer() {
+        return mBuyer;
+    }
+
+    /**
+     * Gets the package name for the app which generated the custom audience which is associated
+     * with the overridden ad counter histogram data.
+     *
+     * <p>For {@link FrequencyCapFilters#AD_EVENT_TYPE_WIN}, ad counter histogram data is restricted
+     * to ads from the same custom audience, which is identified by the buyer from {@link
+     * #getBuyer()}, the custom audience's owner app package name, and the custom audience name from
+     * {@link #getCustomAudienceName()}.
+     */
+    @NonNull
+    public String getCustomAudienceOwner() {
+        return mCustomAudienceOwner;
+    }
+
+    /**
+     * Gets the buyer-generated name for the custom audience which is associated with the overridden
+     * ad counter histogram data.
+     *
+     * <p>For {@link FrequencyCapFilters#AD_EVENT_TYPE_WIN}, ad counter histogram data is restricted
+     * to ads from the same custom audience, which is identified by the buyer from {@link
+     * #getBuyer()}, the custom audience's owner app package name from {@link
+     * #getCustomAudienceOwner()}, and the custom audience name.
+     */
+    @NonNull
+    public String getCustomAudienceName() {
+        return mCustomAudienceName;
+    }
+
+    @Override
+    public String toString() {
+        return "SetAdCounterHistogramOverrideRequest{"
+                + "mAdEventType="
+                + mAdEventType
+                + ", mAdCounterKey="
+                + mAdCounterKey
+                + ", mHistogramTimestamps="
+                + mHistogramTimestamps
+                + ", mBuyer="
+                + mBuyer
+                + ", mCustomAudienceOwner='"
+                + mCustomAudienceOwner
+                + "', mCustomAudienceName='"
+                + mCustomAudienceName
+                + "'}";
+    }
+
+    /** Builder for {@link SetAdCounterHistogramOverrideRequest} objects. */
+    public static final class Builder {
+        @FrequencyCapFilters.AdEventType private int mAdEventType = AD_EVENT_TYPE_INVALID;
+        private int mAdCounterKey;
+        @NonNull private List<Instant> mHistogramTimestamps = new ArrayList<>();
+        @Nullable private AdTechIdentifier mBuyer;
+        @Nullable private String mCustomAudienceOwner;
+        @Nullable private String mCustomAudienceName;
+
+        public Builder() {}
+
+        /**
+         * Sets the {@link FrequencyCapFilters.AdEventType} for the ad counter histogram override.
+         *
+         * <p>See {@link #getAdEventType()} for more information.
+         */
+        @NonNull
+        public Builder setAdEventType(@FrequencyCapFilters.AdEventType int adEventType) {
+            mAdEventType = adEventType;
+            return this;
+        }
+
+        /**
+         * Sets the ad counter key for the ad counter histogram override.
+         *
+         * <p>See {@link #getAdCounterKey()} for more information.
+         */
+        @NonNull
+        public Builder setAdCounterKey(int adCounterKey) {
+            mAdCounterKey = adCounterKey;
+            return this;
+        }
+
+        /**
+         * Sets the list of {@link Instant} objects for the ad counter histogram override.
+         *
+         * <p>See {@link #getHistogramTimestamps()} for more information.
+         */
+        @NonNull
+        public Builder setHistogramTimestamps(@NonNull List<Instant> histogramTimestamps) {
+            Objects.requireNonNull(histogramTimestamps, NULL_HISTOGRAM_TIMESTAMPS_MESSAGE);
+            mHistogramTimestamps = histogramTimestamps;
+            return this;
+        }
+
+        /**
+         * Sets the {@link AdTechIdentifier} for the buyer which owns the ad counter histogram.
+         *
+         * <p>See {@link #getBuyer()} for more information.
+         */
+        @NonNull
+        public Builder setBuyer(@NonNull AdTechIdentifier buyer) {
+            Objects.requireNonNull(buyer, NULL_BUYER_MESSAGE);
+            mBuyer = buyer;
+            return this;
+        }
+
+        /**
+         * Sets the package name for the app which generated the custom audience which is associated
+         * with the overridden ad counter histogram data.
+         *
+         * <p>See {@link #getCustomAudienceOwner()} for more information.
+         */
+        @NonNull
+        public Builder setCustomAudienceOwner(@NonNull String customAudienceOwner) {
+            Objects.requireNonNull(customAudienceOwner, NULL_CUSTOM_AUDIENCE_OWNER_MESSAGE);
+            mCustomAudienceOwner = customAudienceOwner;
+            return this;
+        }
+
+        /**
+         * Sets the buyer-generated name for the custom audience which is associated with the
+         * overridden ad counter histogram data.
+         *
+         * <p>See {@link #getCustomAudienceName()} for more information.
+         */
+        @NonNull
+        public Builder setCustomAudienceName(@NonNull String customAudienceName) {
+            Objects.requireNonNull(customAudienceName, NULL_CUSTOM_AUDIENCE_NAME_MESSAGE);
+            mCustomAudienceName = customAudienceName;
+            return this;
+        }
+
+        /**
+         * Builds the {@link SetAdCounterHistogramOverrideRequest} object.
+         *
+         * @throws NullPointerException if any parameters are not set
+         * @throws IllegalArgumentException if the ad event type is invalid
+         */
+        @NonNull
+        public SetAdCounterHistogramOverrideRequest build()
+                throws NullPointerException, IllegalArgumentException {
+            Preconditions.checkArgument(
+                    mAdEventType != AD_EVENT_TYPE_INVALID, UNSET_AD_EVENT_TYPE_MESSAGE);
+            Objects.requireNonNull(mBuyer, NULL_BUYER_MESSAGE);
+            Objects.requireNonNull(mCustomAudienceOwner, NULL_CUSTOM_AUDIENCE_OWNER_MESSAGE);
+            Objects.requireNonNull(mCustomAudienceName, NULL_CUSTOM_AUDIENCE_NAME_MESSAGE);
+
+            return new SetAdCounterHistogramOverrideRequest(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/SetAppInstallAdvertisersInput.java b/android-35/android/adservices/adselection/SetAppInstallAdvertisersInput.java
new file mode 100644
index 0000000..938c96e
--- /dev/null
+++ b/android-35/android/adservices/adselection/SetAppInstallAdvertisersInput.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import android.adservices.common.AdTechIdentifier;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.AdServicesParcelableUtil;
+
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Represent input params to the setAppInstallAdvertisers API.
+ *
+ * @hide
+ */
+public final class SetAppInstallAdvertisersInput implements Parcelable {
+    @NonNull private final Set<AdTechIdentifier> mAdvertisers;
+    @NonNull private final String mCallerPackageName;
+
+    @NonNull
+    public static final Creator<SetAppInstallAdvertisersInput> CREATOR =
+            new Creator<SetAppInstallAdvertisersInput>() {
+                @NonNull
+                @Override
+                public SetAppInstallAdvertisersInput createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new SetAppInstallAdvertisersInput(in);
+                }
+
+                @NonNull
+                @Override
+                public SetAppInstallAdvertisersInput[] newArray(int size) {
+                    return new SetAppInstallAdvertisersInput[size];
+                }
+            };
+
+    private SetAppInstallAdvertisersInput(
+            @NonNull Set<AdTechIdentifier> advertisers, @NonNull String callerPackageName) {
+        Objects.requireNonNull(advertisers);
+        Objects.requireNonNull(callerPackageName);
+
+        this.mAdvertisers = advertisers;
+        this.mCallerPackageName = callerPackageName;
+    }
+
+    private SetAppInstallAdvertisersInput(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        this.mAdvertisers =
+                AdServicesParcelableUtil.readSetFromParcel(in, AdTechIdentifier.CREATOR);
+        this.mCallerPackageName = in.readString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        AdServicesParcelableUtil.writeSetToParcel(dest, mAdvertisers);
+        dest.writeString(mCallerPackageName);
+    }
+
+    /**
+     * Returns the advertisers, one of the inputs to {@link SetAppInstallAdvertisersInput} as noted
+     * in {@code AdSelectionService}.
+     */
+    @NonNull
+    public Set<AdTechIdentifier> getAdvertisers() {
+        return mAdvertisers;
+    }
+
+    /** @return the caller package name */
+    @NonNull
+    public String getCallerPackageName() {
+        return mCallerPackageName;
+    }
+
+    /**
+     * Builder for {@link SetAppInstallAdvertisersInput} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        @Nullable private Set<AdTechIdentifier> mAdvertisers;
+        @Nullable private String mCallerPackageName;
+
+        public Builder() {}
+
+        /** Set the advertisers. */
+        @NonNull
+        public SetAppInstallAdvertisersInput.Builder setAdvertisers(
+                @NonNull Set<AdTechIdentifier> advertisers) {
+            Objects.requireNonNull(advertisers);
+            this.mAdvertisers = advertisers;
+            return this;
+        }
+
+        /** Sets the caller's package name. */
+        @NonNull
+        public SetAppInstallAdvertisersInput.Builder setCallerPackageName(
+                @NonNull String callerPackageName) {
+            Objects.requireNonNull(callerPackageName);
+
+            this.mCallerPackageName = callerPackageName;
+            return this;
+        }
+
+        /** Builds a {@link SetAppInstallAdvertisersInput} instance. */
+        @NonNull
+        public SetAppInstallAdvertisersInput build() {
+            Objects.requireNonNull(mAdvertisers);
+            Objects.requireNonNull(mCallerPackageName);
+
+            return new SetAppInstallAdvertisersInput(mAdvertisers, mCallerPackageName);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/SetAppInstallAdvertisersRequest.java b/android-35/android/adservices/adselection/SetAppInstallAdvertisersRequest.java
new file mode 100644
index 0000000..8cbcb5e
--- /dev/null
+++ b/android-35/android/adservices/adselection/SetAppInstallAdvertisersRequest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import android.adservices.common.AdTechIdentifier;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.adservices.flags.Flags;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/** Represents input parameters to the setAppInstallAdvertiser API. */
+@FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
+public class SetAppInstallAdvertisersRequest {
+    @NonNull private final Set<AdTechIdentifier> mAdvertisers;
+
+    private SetAppInstallAdvertisersRequest(@NonNull Set<AdTechIdentifier> advertisers) {
+        Objects.requireNonNull(advertisers);
+
+        mAdvertisers = new HashSet<>(advertisers);
+    }
+
+    /**
+     * Returns the set of advertisers that will be able to run app install filters based on this
+     * app's presence on the device after a call to SetAppInstallAdvertisers is made with this as
+     * input.
+     */
+    @NonNull
+    public Set<AdTechIdentifier> getAdvertisers() {
+        return mAdvertisers;
+    }
+
+    public static final class Builder {
+        @Nullable private Set<AdTechIdentifier> mAdvertisers;
+
+        public Builder() {}
+
+        /**
+         * Sets list of allowed advertisers. See {@link SetAppInstallAdvertisersRequest
+         * #getAdvertisers()}
+         */
+        @NonNull
+        public SetAppInstallAdvertisersRequest.Builder setAdvertisers(
+                @NonNull Set<AdTechIdentifier> advertisers) {
+            Objects.requireNonNull(advertisers);
+
+            mAdvertisers = new HashSet<>(advertisers);
+            return this;
+        }
+
+        /** Builds a {@link SetAppInstallAdvertisersRequest} instance. */
+        @NonNull
+        public SetAppInstallAdvertisersRequest build() {
+            return new SetAppInstallAdvertisersRequest(mAdvertisers);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/SignedContextualAds.java b/android-35/android/adservices/adselection/SignedContextualAds.java
new file mode 100644
index 0000000..45a7a89
--- /dev/null
+++ b/android-35/android/adservices/adselection/SignedContextualAds.java
@@ -0,0 +1,269 @@
+/*
+ * 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 android.adservices.adselection;
+
+import android.adservices.common.AdTechIdentifier;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.flags.Flags;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Contains a list of buyer supplied {@link AdWithBid} bundle and its signature.
+ *
+ * <p>Instances of this class are created by SDKs to be injected as part of {@link
+ * AdSelectionConfig} and passed to {@link AdSelectionManager#selectAds}
+ *
+ * <p>SignedContextualAds are signed using ECDSA algorithm with SHA256 hashing algorithm (aka
+ * SHA256withECDSA). Keys used should belong to P-256 curve (aka “secp256r1” or “prime256v1”).
+ *
+ * <p>Signature should include the buyer, decisionLogicUri and adsWithBid fields.
+ *
+ * <p>While creating the signature a specific serialization rules must be followed as it's outlined
+ * here:
+ *
+ * <ul>
+ *   <li>{@code Objects} concatenate the serialized values of their fields with the {@code |} (pipe)
+ *       in between each field
+ *   <li>{@code All fields} are sorted by alphabetical order within the object
+ *   <li>{@code Nullable fields} are skipped if they are null/unset
+ *   <li>{@code Doubles} are converted to String preserving precision
+ *   <li>{@code Integers} are converted to string values
+ *   <li>{@code Sets} are sorted alphabetically
+ *   <li>{@code Lists} keep the same order
+ *   <li>{@code Strings} get encoded into byte[] using UTF-8 encoding
+ * </ul>
+ */
+@FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
+public final class SignedContextualAds implements Parcelable {
+    private static final String BUYER_CANNOT_BE_NULL = "Buyer cannot be null.";
+    private static final String DECISION_LOGIC_URI_CANNOT_BE_NULL =
+            "DecisionLogicUri cannot be null.";
+    private static final String ADS_WITH_BID_CANNOT_BE_NULL = "AdsWithBid cannot be null.";
+    private static final String SIGNATURE_CANNOT_BE_NULL = "Signature cannot be null.";
+    @NonNull private final AdTechIdentifier mBuyer;
+    @NonNull private final Uri mDecisionLogicUri;
+    @NonNull private final List<AdWithBid> mAdsWithBid;
+    @NonNull private final byte[] mSignature;
+
+    @NonNull
+    public static final Creator<SignedContextualAds> CREATOR =
+            new Creator<>() {
+                @Override
+                public SignedContextualAds createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new SignedContextualAds(in);
+                }
+
+                @Override
+                public SignedContextualAds[] newArray(int size) {
+                    return new SignedContextualAds[0];
+                }
+            };
+
+    private SignedContextualAds(
+            @NonNull AdTechIdentifier buyer,
+            @NonNull Uri decisionLogicUri,
+            @NonNull List<AdWithBid> adsWithBid,
+            @NonNull byte[] signature) {
+        this.mBuyer = buyer;
+        this.mDecisionLogicUri = decisionLogicUri;
+        this.mAdsWithBid = adsWithBid;
+        this.mSignature = signature;
+    }
+
+    private SignedContextualAds(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+        mBuyer = AdTechIdentifier.CREATOR.createFromParcel(in);
+        mDecisionLogicUri = Uri.CREATOR.createFromParcel(in);
+        mAdsWithBid = in.createTypedArrayList(AdWithBid.CREATOR);
+        mSignature = in.createByteArray();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        mBuyer.writeToParcel(dest, flags);
+        mDecisionLogicUri.writeToParcel(dest, flags);
+        dest.writeTypedList(mAdsWithBid);
+        dest.writeByteArray(mSignature);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SignedContextualAds)) return false;
+        SignedContextualAds that = (SignedContextualAds) o;
+        return Objects.equals(mBuyer, that.mBuyer)
+                && Objects.equals(mDecisionLogicUri, that.mDecisionLogicUri)
+                && Objects.equals(mAdsWithBid, that.mAdsWithBid)
+                && Arrays.equals(mSignature, that.mSignature);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mBuyer, mDecisionLogicUri, mAdsWithBid, Arrays.hashCode(mSignature));
+    }
+
+    /**
+     * @return the Ad tech identifier from which this contextual Ad would have been downloaded
+     */
+    @NonNull
+    public AdTechIdentifier getBuyer() {
+        return mBuyer;
+    }
+
+    /**
+     * @return the URI used to retrieve the updateBid() and reportWin() function used during the ad
+     *     selection and reporting process
+     */
+    @NonNull
+    public Uri getDecisionLogicUri() {
+        return mDecisionLogicUri;
+    }
+
+    /**
+     * @return the Ad data with bid value associated with this ad
+     */
+    @NonNull
+    public List<AdWithBid> getAdsWithBid() {
+        return mAdsWithBid;
+    }
+
+    /**
+     * Returns a copy of the signature for the contextual ads object.
+     *
+     * <p>See {@link SignedContextualAds} for more details.
+     *
+     * @return the signature
+     */
+    @NonNull
+    public byte[] getSignature() {
+        return Arrays.copyOf(mSignature, mSignature.length);
+    }
+
+    @Override
+    public String toString() {
+        return "SignedContextualAds{"
+                + "mBuyer="
+                + mBuyer
+                + ", mDecisionLogicUri="
+                + mDecisionLogicUri
+                + ", mAdsWithBid="
+                + mAdsWithBid
+                + ", mSignature="
+                + Arrays.toString(mSignature)
+                + '}';
+    }
+
+    /** Builder for {@link SignedContextualAds} object */
+    public static final class Builder {
+        @Nullable private AdTechIdentifier mBuyer;
+        @Nullable private Uri mDecisionLogicUri;
+        @Nullable private List<AdWithBid> mAdsWithBid;
+        @Nullable private byte[] mSignature;
+
+        public Builder() {}
+
+        /** Returns a {@link SignedContextualAds.Builder} from a {@link SignedContextualAds}. */
+        public Builder(@NonNull SignedContextualAds signedContextualAds) {
+            Objects.requireNonNull(signedContextualAds);
+
+            this.mBuyer = signedContextualAds.getBuyer();
+            this.mDecisionLogicUri = signedContextualAds.getDecisionLogicUri();
+            this.mAdsWithBid = signedContextualAds.getAdsWithBid();
+            this.mSignature = signedContextualAds.getSignature();
+        }
+
+        /**
+         * Sets the buyer Ad tech Identifier
+         *
+         * <p>See {@link #getBuyer()} for more details
+         */
+        @NonNull
+        public SignedContextualAds.Builder setBuyer(@NonNull AdTechIdentifier buyer) {
+            Objects.requireNonNull(buyer, BUYER_CANNOT_BE_NULL);
+
+            this.mBuyer = buyer;
+            return this;
+        }
+
+        /**
+         * Sets the URI to fetch the decision logic used in ad selection and reporting
+         *
+         * <p>See {@link #getDecisionLogicUri()} for more details
+         */
+        @NonNull
+        public SignedContextualAds.Builder setDecisionLogicUri(@NonNull Uri decisionLogicUri) {
+            Objects.requireNonNull(decisionLogicUri, DECISION_LOGIC_URI_CANNOT_BE_NULL);
+
+            this.mDecisionLogicUri = decisionLogicUri;
+            return this;
+        }
+
+        /**
+         * Sets the Ads with pre-defined bid values
+         *
+         * <p>See {@link #getAdsWithBid()} for more details
+         */
+        @NonNull
+        public SignedContextualAds.Builder setAdsWithBid(@NonNull List<AdWithBid> adsWithBid) {
+            Objects.requireNonNull(adsWithBid, ADS_WITH_BID_CANNOT_BE_NULL);
+
+            this.mAdsWithBid = adsWithBid;
+            return this;
+        }
+
+        /** Sets the copied signature */
+        @NonNull
+        public SignedContextualAds.Builder setSignature(@NonNull byte[] signature) {
+            Objects.requireNonNull(signature, SIGNATURE_CANNOT_BE_NULL);
+
+            this.mSignature = Arrays.copyOf(signature, signature.length);
+            return this;
+        }
+
+        /**
+         * Builds a {@link SignedContextualAds} instance.
+         *
+         * @throws NullPointerException if any required params are null
+         */
+        @NonNull
+        public SignedContextualAds build() {
+            Objects.requireNonNull(mBuyer, BUYER_CANNOT_BE_NULL);
+            Objects.requireNonNull(mDecisionLogicUri, DECISION_LOGIC_URI_CANNOT_BE_NULL);
+            Objects.requireNonNull(mAdsWithBid, ADS_WITH_BID_CANNOT_BE_NULL);
+            Objects.requireNonNull(mSignature, SIGNATURE_CANNOT_BE_NULL);
+
+            return new SignedContextualAds(mBuyer, mDecisionLogicUri, mAdsWithBid, mSignature);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/TestAdSelectionManager.java b/android-35/android/adservices/adselection/TestAdSelectionManager.java
new file mode 100644
index 0000000..c45775c
--- /dev/null
+++ b/android-35/android/adservices/adselection/TestAdSelectionManager.java
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.adselection;
+
+import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_SELECTION;
+import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE;
+import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_PROTECTED_SIGNALS;
+
+import android.adservices.common.AdServicesStatusUtils;
+import android.adservices.common.FledgeErrorResponse;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.os.Build;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.adservices.LoggerFactory;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * {@link TestAdSelectionManager} provides APIs for apps and ad SDKs to test ad selection processes.
+ *
+ * <p>These APIs are intended to be used for end-to-end testing. They are enabled only for
+ * debuggable apps on phones running a debuggable OS build with developer options enabled.
+ */
+@RequiresApi(Build.VERSION_CODES.S)
+public class TestAdSelectionManager {
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger();
+
+    private final AdSelectionManager mAdSelectionManager;
+
+    TestAdSelectionManager(@NonNull AdSelectionManager adSelectionManager) {
+        Objects.requireNonNull(adSelectionManager);
+
+        mAdSelectionManager = adSelectionManager;
+    }
+
+    // TODO(b/289362476): Add override APIs for server auction key fetch
+
+    /**
+     * Overrides the AdSelection API for a given {@link AdSelectionConfig} to avoid fetching data
+     * from remote servers and use the data provided in {@link AddAdSelectionOverrideRequest}
+     * instead. The {@link AddAdSelectionOverrideRequest} is provided by the Ads SDK.
+     *
+     * <p>This method is intended to be used for end-to-end testing. This API is enabled only for
+     * apps in debug mode with developer options enabled.
+     *
+     * @throws IllegalStateException if this API is not enabled for the caller
+     *     <p>The receiver either returns a {@code void} for a successful run, or an {@link
+     *     Exception} indicates the error.
+     */
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void overrideAdSelectionConfigRemoteInfo(
+            @NonNull AddAdSelectionOverrideRequest request,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+
+        try {
+            final AdSelectionService service =
+                    mAdSelectionManager.getServiceProvider().getService();
+            service.overrideAdSelectionConfigRemoteInfo(
+                    request.getAdSelectionConfig(),
+                    request.getDecisionLogicJs(),
+                    request.getTrustedScoringSignals(),
+                    request.getPerBuyerDecisionLogic(),
+                    new AdSelectionOverrideCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            receiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service.");
+            receiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service.", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Failure of AdSelection service.", e));
+        }
+    }
+
+    /**
+     * Removes an override for {@link AdSelectionConfig} in the Ad Selection API with associated the
+     * data in {@link RemoveAdSelectionOverrideRequest}. The {@link
+     * RemoveAdSelectionOverrideRequest} is provided by the Ads SDK.
+     *
+     * <p>This method is intended to be used for end-to-end testing. This API is enabled only for
+     * apps in debug mode with developer options enabled.
+     *
+     * @throws IllegalStateException if this API is not enabled for the caller
+     *     <p>The receiver either returns a {@code void} for a successful run, or an {@link
+     *     Exception} indicates the error.
+     */
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void removeAdSelectionConfigRemoteInfoOverride(
+            @NonNull RemoveAdSelectionOverrideRequest request,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+
+        try {
+            final AdSelectionService service =
+                    mAdSelectionManager.getServiceProvider().getService();
+            service.removeAdSelectionConfigRemoteInfoOverride(
+                    request.getAdSelectionConfig(),
+                    new AdSelectionOverrideCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            receiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service.");
+            receiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service.", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Failure of AdSelection service.", e));
+        }
+    }
+
+    /**
+     * Removes all override data for {@link AdSelectionConfig} in the Ad Selection API.
+     *
+     * <p>This method is intended to be used for end-to-end testing. This API is enabled only for
+     * apps in debug mode with developer options enabled.
+     *
+     * @throws IllegalStateException if this API is not enabled for the caller
+     *     <p>The receiver either returns a {@code void} for a successful run, or an {@link
+     *     Exception} indicates the error.
+     */
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void resetAllAdSelectionConfigRemoteOverrides(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+
+        try {
+            final AdSelectionService service =
+                    mAdSelectionManager.getServiceProvider().getService();
+            service.resetAllAdSelectionConfigRemoteOverrides(
+                    new AdSelectionOverrideCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            receiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service.");
+            receiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service.", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Failure of AdSelection service.", e));
+        }
+    }
+
+    /**
+     * Overrides the AdSelection API for {@link AdSelectionFromOutcomesConfig} to avoid fetching
+     * data from remote servers and use the data provided in {@link
+     * AddAdSelectionFromOutcomesOverrideRequest} instead. The {@link
+     * AddAdSelectionFromOutcomesOverrideRequest} is provided by the Ads SDK.
+     *
+     * <p>This method is intended to be used for end-to-end testing. This API is enabled only for
+     * apps in debug mode with developer options enabled.
+     *
+     * @throws IllegalStateException if this API is not enabled for the caller
+     *     <p>The receiver either returns a {@code void} for a successful run, or an {@link
+     *     Exception} indicates the error.
+     */
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void overrideAdSelectionFromOutcomesConfigRemoteInfo(
+            @NonNull AddAdSelectionFromOutcomesOverrideRequest request,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+
+        try {
+            final AdSelectionService service =
+                    mAdSelectionManager.getServiceProvider().getService();
+            service.overrideAdSelectionFromOutcomesConfigRemoteInfo(
+                    request.getAdSelectionFromOutcomesConfig(),
+                    request.getOutcomeSelectionLogicJs(),
+                    request.getOutcomeSelectionTrustedSignals(),
+                    new AdSelectionOverrideCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            receiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service.");
+            receiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service.", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Failure of AdSelection service.", e));
+        }
+    }
+
+    /**
+     * Removes an override for {@link AdSelectionFromOutcomesConfig} in th Ad Selection API with
+     * associated the data in {@link RemoveAdSelectionOverrideRequest}. The {@link
+     * RemoveAdSelectionOverrideRequest} is provided by the Ads SDK.
+     *
+     * <p>This method is intended to be used for end-to-end testing. This API is enabled only for
+     * apps in debug mode with developer options enabled.
+     *
+     * @throws IllegalStateException if this API is not enabled for the caller
+     *     <p>The receiver either returns a {@code void} for a successful run, or an {@link
+     *     Exception} indicates the error.
+     */
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void removeAdSelectionFromOutcomesConfigRemoteInfoOverride(
+            @NonNull RemoveAdSelectionFromOutcomesOverrideRequest request,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+
+        try {
+            final AdSelectionService service =
+                    mAdSelectionManager.getServiceProvider().getService();
+            service.removeAdSelectionFromOutcomesConfigRemoteInfoOverride(
+                    request.getAdSelectionFromOutcomesConfig(),
+                    new AdSelectionOverrideCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            receiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service.");
+            receiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service.", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Failure of AdSelection service.", e));
+        }
+    }
+
+    /**
+     * Removes all override data for {@link AdSelectionFromOutcomesConfig} in the Ad Selection API.
+     *
+     * <p>This method is intended to be used for end-to-end testing. This API is enabled only for
+     * apps in debug mode with developer options enabled.
+     *
+     * @throws IllegalStateException if this API is not enabled for the caller
+     *     <p>The receiver either returns a {@code void} for a successful run, or an {@link
+     *     Exception} indicates the error.
+     */
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void resetAllAdSelectionFromOutcomesConfigRemoteOverrides(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+
+        try {
+            final AdSelectionService service =
+                    mAdSelectionManager.getServiceProvider().getService();
+            service.resetAllAdSelectionFromOutcomesConfigRemoteOverrides(
+                    new AdSelectionOverrideCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            receiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service.");
+            receiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service.", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Failure of AdSelection service.", e));
+        }
+    }
+
+    /**
+     * Sets the override for event histogram data, which is used in frequency cap filtering during
+     * ad selection.
+     *
+     * <p>This method is intended to be used for end-to-end testing. This API is enabled only for
+     * apps in debug mode with developer options enabled.
+     *
+     * <p>The given {@code outcomeReceiver} either returns an empty {@link Object} if successful or
+     * an {@link Exception} which indicates the error.
+     *
+     * @throws IllegalStateException if this API is not enabled for the caller
+     * @hide
+     */
+    // TODO(b/265204820): Unhide for frequency cap dev override API review
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void setAdCounterHistogramOverride(
+            @NonNull SetAdCounterHistogramOverrideRequest setRequest,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> outcomeReceiver) {
+        Objects.requireNonNull(setRequest, "Request must not be null");
+        Objects.requireNonNull(executor, "Executor must not be null");
+        Objects.requireNonNull(outcomeReceiver, "Outcome receiver must not be null");
+
+        try {
+            final AdSelectionService service =
+                    Objects.requireNonNull(mAdSelectionManager.getServiceProvider().getService());
+            service.setAdCounterHistogramOverride(
+                    new SetAdCounterHistogramOverrideInput.Builder()
+                            .setAdEventType(setRequest.getAdEventType())
+                            .setAdCounterKey(setRequest.getAdCounterKey())
+                            .setHistogramTimestamps(setRequest.getHistogramTimestamps())
+                            .setBuyer(setRequest.getBuyer())
+                            .setCustomAudienceOwner(setRequest.getCustomAudienceOwner())
+                            .setCustomAudienceName(setRequest.getCustomAudienceName())
+                            .build(),
+                    new AdSelectionOverrideCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> outcomeReceiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            outcomeReceiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service");
+            outcomeReceiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Remote exception encountered while updating ad counter histogram");
+            outcomeReceiver.onError(new IllegalStateException("Failure of AdSelection service", e));
+        }
+    }
+
+    /**
+     * Removes an override for event histogram data, which is used in frequency cap filtering during
+     * ad selection.
+     *
+     * <p>This method is intended to be used for end-to-end testing. This API is enabled only for
+     * apps in debug mode with developer options enabled.
+     *
+     * <p>The given {@code outcomeReceiver} either returns an empty {@link Object} if successful or
+     * an {@link Exception} which indicates the error.
+     *
+     * @throws IllegalStateException if this API is not enabled for the caller
+     * @hide
+     */
+    // TODO(b/265204820): Unhide for frequency cap dev override API review
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void removeAdCounterHistogramOverride(
+            @NonNull RemoveAdCounterHistogramOverrideRequest removeRequest,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> outcomeReceiver) {
+        Objects.requireNonNull(removeRequest, "Request must not be null");
+        Objects.requireNonNull(executor, "Executor must not be null");
+        Objects.requireNonNull(outcomeReceiver, "Outcome receiver must not be null");
+
+        try {
+            final AdSelectionService service =
+                    Objects.requireNonNull(mAdSelectionManager.getServiceProvider().getService());
+            service.removeAdCounterHistogramOverride(
+                    new RemoveAdCounterHistogramOverrideInput.Builder()
+                            .setAdEventType(removeRequest.getAdEventType())
+                            .setAdCounterKey(removeRequest.getAdCounterKey())
+                            .setBuyer(removeRequest.getBuyer())
+                            .build(),
+                    new AdSelectionOverrideCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> outcomeReceiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            outcomeReceiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service");
+            outcomeReceiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Remote exception encountered while updating ad counter histogram");
+            outcomeReceiver.onError(new IllegalStateException("Failure of AdSelection service", e));
+        }
+    }
+
+    /**
+     * Removes all previously set histogram overrides used in ad selection which were set by the
+     * caller application.
+     *
+     * <p>This method is intended to be used for end-to-end testing. This API is enabled only for
+     * apps in debug mode with developer options enabled.
+     *
+     * <p>The given {@code outcomeReceiver} either returns an empty {@link Object} if successful or
+     * an {@link Exception} which indicates the error.
+     *
+     * @throws IllegalStateException if this API is not enabled for the caller
+     * @hide
+     */
+    // TODO(b/265204820): Unhide for frequency cap dev override API review
+    @RequiresPermission(
+            anyOf = {
+                ACCESS_ADSERVICES_CUSTOM_AUDIENCE,
+                ACCESS_ADSERVICES_PROTECTED_SIGNALS,
+                ACCESS_ADSERVICES_AD_SELECTION
+            })
+    public void resetAllAdCounterHistogramOverrides(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> outcomeReceiver) {
+        Objects.requireNonNull(executor, "Executor must not be null");
+        Objects.requireNonNull(outcomeReceiver, "Outcome receiver must not be null");
+
+        try {
+            final AdSelectionService service =
+                    Objects.requireNonNull(mAdSelectionManager.getServiceProvider().getService());
+            service.resetAllAdCounterHistogramOverrides(
+                    new AdSelectionOverrideCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> outcomeReceiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            outcomeReceiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (NullPointerException e) {
+            sLogger.e(e, "Unable to find the AdSelection service");
+            outcomeReceiver.onError(
+                    new IllegalStateException("Unable to find the AdSelection service", e));
+        } catch (RemoteException e) {
+            sLogger.e(e, "Remote exception encountered while updating ad counter histogram");
+            outcomeReceiver.onError(new IllegalStateException("Failure of AdSelection service", e));
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/UpdateAdCounterHistogramInput.java b/android-35/android/adservices/adselection/UpdateAdCounterHistogramInput.java
new file mode 100644
index 0000000..8581bf0
--- /dev/null
+++ b/android-35/android/adservices/adselection/UpdateAdCounterHistogramInput.java
@@ -0,0 +1,278 @@
+/*
+ * 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 android.adservices.adselection;
+
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID;
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID_MESSAGE;
+import static android.adservices.adselection.UpdateAdCounterHistogramRequest.DISALLOW_AD_EVENT_TYPE_WIN_MESSAGE;
+import static android.adservices.adselection.UpdateAdCounterHistogramRequest.INVALID_AD_EVENT_TYPE_MESSAGE;
+import static android.adservices.adselection.UpdateAdCounterHistogramRequest.UNSET_CALLER_ADTECH_MESSAGE;
+import static android.adservices.common.FrequencyCapFilters.AD_EVENT_TYPE_WIN;
+
+import android.adservices.common.AdTechIdentifier;
+import android.adservices.common.FrequencyCapFilters;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Input object wrapping the required arguments needed to update an ad counter histogram.
+ *
+ * <p>The ad counter histograms, which are historical logs of events which are associated with an ad
+ * counter key and an ad event type, are used to inform frequency cap filtering when using the
+ * Protected Audience APIs.
+ *
+ * @hide
+ */
+public final class UpdateAdCounterHistogramInput implements Parcelable {
+    private static final String UNSET_CALLER_PACKAGE_NAME_MESSAGE =
+            "Caller package name must not be null";
+
+    private final long mAdSelectionId;
+    @FrequencyCapFilters.AdEventType private final int mAdEventType;
+    @NonNull private final AdTechIdentifier mCallerAdTech;
+    @NonNull private final String mCallerPackageName;
+
+    @NonNull
+    public static final Creator<UpdateAdCounterHistogramInput> CREATOR =
+            new Creator<UpdateAdCounterHistogramInput>() {
+                @Override
+                public UpdateAdCounterHistogramInput createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+
+                    return new UpdateAdCounterHistogramInput(in);
+                }
+
+                @Override
+                public UpdateAdCounterHistogramInput[] newArray(int size) {
+                    return new UpdateAdCounterHistogramInput[size];
+                }
+            };
+
+    private UpdateAdCounterHistogramInput(@NonNull Builder builder) {
+        Objects.requireNonNull(builder);
+
+        mAdSelectionId = builder.mAdSelectionId;
+        mAdEventType = builder.mAdEventType;
+        mCallerAdTech = builder.mCallerAdTech;
+        mCallerPackageName = builder.mCallerPackageName;
+    }
+
+    private UpdateAdCounterHistogramInput(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        mAdSelectionId = in.readLong();
+        mAdEventType = in.readInt();
+        mCallerAdTech = AdTechIdentifier.CREATOR.createFromParcel(in);
+        mCallerPackageName = in.readString();
+    }
+
+    /**
+     * Gets the ad selection ID with which the rendered ad's events are associated.
+     *
+     * <p>The ad must have been selected from Protected Audience ad selection in the last 24 hours,
+     * and the ad selection call must have been initiated from the same app as the current calling
+     * app. Event histograms for all ad counter keys associated with the ad specified by the ad
+     * selection ID will be updated for the ad event type from {@link #getAdEventType()}, to be used
+     * in Protected Audience frequency cap filtering.
+     */
+    public long getAdSelectionId() {
+        return mAdSelectionId;
+    }
+
+    /**
+     * Gets the {@link android.adservices.common.FrequencyCapFilters.AdEventType} which, along with
+     * an ad's counter keys, identifies which histogram should be updated.
+     *
+     * <p>See {@link android.adservices.common.FrequencyCapFilters.AdEventType} for more
+     * information.
+     */
+    @FrequencyCapFilters.AdEventType
+    public int getAdEventType() {
+        return mAdEventType;
+    }
+
+    /**
+     * Gets the caller adtech entity's {@link AdTechIdentifier}.
+     *
+     * <p>The adtech using this {@link UpdateAdCounterHistogramInput} object must have enrolled with
+     * the Privacy Sandbox and be allowed to act on behalf of the calling app. The specified adtech
+     * is not required to be the same adtech as either the buyer which owns the rendered ad or the
+     * seller which initiated the ad selection associated with the ID returned by {@link
+     * #getAdSelectionId()}.
+     */
+    @NonNull
+    public AdTechIdentifier getCallerAdTech() {
+        return mCallerAdTech;
+    }
+
+    /**
+     * Gets the caller app's package name.
+     *
+     * <p>The package name must match the caller package name for the Protected Audience ad
+     * selection represented by the ID returned by {@link #getAdSelectionId()}.
+     */
+    @NonNull
+    public String getCallerPackageName() {
+        return mCallerPackageName;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(mAdSelectionId);
+        dest.writeInt(mAdEventType);
+        mCallerAdTech.writeToParcel(dest, flags);
+        dest.writeString(mCallerPackageName);
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Checks whether the {@link UpdateAdCounterHistogramInput} objects contain the same
+     * information.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof UpdateAdCounterHistogramInput)) return false;
+        UpdateAdCounterHistogramInput that = (UpdateAdCounterHistogramInput) o;
+        return mAdSelectionId == that.mAdSelectionId
+                && mAdEventType == that.mAdEventType
+                && mCallerAdTech.equals(that.mCallerAdTech)
+                && mCallerPackageName.equals(that.mCallerPackageName);
+    }
+
+    /** Returns the hash of the {@link UpdateAdCounterHistogramInput} object's data. */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAdSelectionId, mAdEventType, mCallerAdTech, mCallerPackageName);
+    }
+
+    @Override
+    public String toString() {
+        return "UpdateAdCounterHistogramInput{"
+                + "mAdSelectionId="
+                + mAdSelectionId
+                + ", mAdEventType="
+                + mAdEventType
+                + ", mCallerAdTech="
+                + mCallerAdTech
+                + ", mCallerPackageName='"
+                + mCallerPackageName
+                + '\''
+                + '}';
+    }
+
+    /** Builder for {@link UpdateAdCounterHistogramInput} objects. */
+    public static final class Builder {
+        private long mAdSelectionId;
+        @FrequencyCapFilters.AdEventType private int mAdEventType;
+        @NonNull private AdTechIdentifier mCallerAdTech;
+        @NonNull private String mCallerPackageName;
+
+        public Builder(
+                long adSelectionId,
+                int adEventType,
+                @NonNull AdTechIdentifier callerAdTech,
+                @NonNull String callerPackageName) {
+            Preconditions.checkArgument(
+                    adSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+            Preconditions.checkArgument(
+                    adEventType != AD_EVENT_TYPE_WIN, DISALLOW_AD_EVENT_TYPE_WIN_MESSAGE);
+            Preconditions.checkArgument(
+                    adEventType >= FrequencyCapFilters.AD_EVENT_TYPE_MIN
+                            && adEventType <= FrequencyCapFilters.AD_EVENT_TYPE_MAX,
+                    INVALID_AD_EVENT_TYPE_MESSAGE);
+            Objects.requireNonNull(callerAdTech, UNSET_CALLER_ADTECH_MESSAGE);
+            Objects.requireNonNull(callerPackageName, UNSET_CALLER_PACKAGE_NAME_MESSAGE);
+
+            mAdSelectionId = adSelectionId;
+            mAdEventType = adEventType;
+            mCallerAdTech = callerAdTech;
+            mCallerPackageName = callerPackageName;
+        }
+
+        /**
+         * Sets the ad selection ID with which the rendered ad's events are associated.
+         *
+         * <p>See {@link #getAdSelectionId()} for more information.
+         */
+        @NonNull
+        public Builder setAdSelectionId(long adSelectionId) {
+            Preconditions.checkArgument(
+                    adSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+            mAdSelectionId = adSelectionId;
+            return this;
+        }
+
+        /**
+         * Sets the {@link android.adservices.common.FrequencyCapFilters.AdEventType} which, along
+         * with an ad's counter keys, identifies which histogram should be updated.
+         *
+         * <p>See {@link #getAdEventType()} for more information.
+         */
+        @NonNull
+        public Builder setAdEventType(@FrequencyCapFilters.AdEventType int adEventType) {
+            Preconditions.checkArgument(
+                    adEventType != AD_EVENT_TYPE_WIN, DISALLOW_AD_EVENT_TYPE_WIN_MESSAGE);
+            Preconditions.checkArgument(
+                    adEventType >= FrequencyCapFilters.AD_EVENT_TYPE_MIN
+                            && adEventType <= FrequencyCapFilters.AD_EVENT_TYPE_MAX,
+                    INVALID_AD_EVENT_TYPE_MESSAGE);
+            mAdEventType = adEventType;
+            return this;
+        }
+
+        /**
+         * Sets the caller adtech entity's {@link AdTechIdentifier}.
+         *
+         * <p>See {@link #getCallerAdTech()} for more information.
+         */
+        @NonNull
+        public Builder setCallerAdTech(@NonNull AdTechIdentifier callerAdTech) {
+            Objects.requireNonNull(callerAdTech, UNSET_CALLER_ADTECH_MESSAGE);
+            mCallerAdTech = callerAdTech;
+            return this;
+        }
+
+        /**
+         * Sets the caller app's package name.
+         *
+         * <p>See {@link #getCallerPackageName()} for more information.
+         */
+        @NonNull
+        public Builder setCallerPackageName(@NonNull String callerPackageName) {
+            Objects.requireNonNull(callerPackageName, UNSET_CALLER_PACKAGE_NAME_MESSAGE);
+            mCallerPackageName = callerPackageName;
+            return this;
+        }
+
+        /** Builds the {@link UpdateAdCounterHistogramInput} object. */
+        @NonNull
+        public UpdateAdCounterHistogramInput build() {
+            return new UpdateAdCounterHistogramInput(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/adselection/UpdateAdCounterHistogramRequest.java b/android-35/android/adservices/adselection/UpdateAdCounterHistogramRequest.java
new file mode 100644
index 0000000..b661648
--- /dev/null
+++ b/android-35/android/adservices/adselection/UpdateAdCounterHistogramRequest.java
@@ -0,0 +1,214 @@
+/*
+ * 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 android.adservices.adselection;
+
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID;
+import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID_MESSAGE;
+import static android.adservices.common.FrequencyCapFilters.AD_EVENT_TYPE_WIN;
+
+import android.adservices.common.AdTechIdentifier;
+import android.adservices.common.FrequencyCapFilters;
+import android.annotation.NonNull;
+import android.os.OutcomeReceiver;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Request object wrapping the required arguments needed to update an ad counter histogram.
+ *
+ * <p>The ad counter histograms, which are historical logs of events which are associated with an ad
+ * counter key and an ad event type, are used to inform frequency cap filtering when using the
+ * Protected Audience APIs.
+ */
+public class UpdateAdCounterHistogramRequest {
+    /** @hide */
+    public static final String UNSET_AD_EVENT_TYPE_MESSAGE = "Ad event type must be set";
+
+    /** @hide */
+    public static final String DISALLOW_AD_EVENT_TYPE_WIN_MESSAGE =
+            "Win event types cannot be manually updated";
+
+    /** @hide */
+    public static final String INVALID_AD_EVENT_TYPE_MESSAGE =
+            "Ad event type must be one of AD_EVENT_TYPE_IMPRESSION, AD_EVENT_TYPE_VIEW, or"
+                    + " AD_EVENT_TYPE_CLICK";
+
+    /** @hide */
+    public static final String UNSET_CALLER_ADTECH_MESSAGE = "Caller ad tech must not be null";
+
+    private final long mAdSelectionId;
+    @FrequencyCapFilters.AdEventType private final int mAdEventType;
+    @NonNull private final AdTechIdentifier mCallerAdTech;
+
+    private UpdateAdCounterHistogramRequest(@NonNull Builder builder) {
+        Objects.requireNonNull(builder);
+
+        mAdSelectionId = builder.mAdSelectionId;
+        mAdEventType = builder.mAdEventType;
+        mCallerAdTech = builder.mCallerAdTech;
+    }
+
+    /**
+     * Gets the ad selection ID with which the rendered ad's events are associated.
+     *
+     * <p>For more information about the ad selection ID, see {@link AdSelectionOutcome}.
+     *
+     * <p>The ad must have been selected from Protected Audience ad selection in the last 24 hours,
+     * and the ad selection call must have been initiated from the same app as the current calling
+     * app. Event histograms for all ad counter keys associated with the ad specified by the ad
+     * selection ID will be updated for the ad event type from {@link #getAdEventType()}, to be used
+     * in Protected Audience frequency cap filtering.
+     */
+    public long getAdSelectionId() {
+        return mAdSelectionId;
+    }
+
+    /**
+     * Gets the ad event type which, along with an ad's counter keys, identifies which histogram
+     * should be updated.
+     */
+    @FrequencyCapFilters.AdEventType
+    public int getAdEventType() {
+        return mAdEventType;
+    }
+
+    /**
+     * Gets the caller adtech entity's {@link AdTechIdentifier}.
+     *
+     * <p>The adtech using this {@link UpdateAdCounterHistogramRequest} object must have enrolled
+     * with the Privacy Sandbox and be allowed to act on behalf of the calling app. The specified
+     * adtech is not required to be the same adtech as either the buyer which owns the rendered ad
+     * or the seller which initiated the ad selection associated with the ID returned by {@link
+     * #getAdSelectionId()}.
+     *
+     * <p>For more information about API requirements and exceptions, see {@link
+     * AdSelectionManager#updateAdCounterHistogram(UpdateAdCounterHistogramRequest, Executor,
+     * OutcomeReceiver)}.
+     */
+    @NonNull
+    public AdTechIdentifier getCallerAdTech() {
+        return mCallerAdTech;
+    }
+
+    /**
+     * Checks whether the {@link UpdateAdCounterHistogramRequest} objects contain the same
+     * information.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof UpdateAdCounterHistogramRequest)) return false;
+        UpdateAdCounterHistogramRequest that = (UpdateAdCounterHistogramRequest) o;
+        return mAdSelectionId == that.mAdSelectionId
+                && mAdEventType == that.mAdEventType
+                && mCallerAdTech.equals(that.mCallerAdTech);
+    }
+
+    /** Returns the hash of the {@link UpdateAdCounterHistogramRequest} object's data. */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAdSelectionId, mAdEventType, mCallerAdTech);
+    }
+
+    @Override
+    public String toString() {
+        return "UpdateAdCounterHistogramRequest{"
+                + "mAdSelectionId="
+                + mAdSelectionId
+                + ", mAdEventType="
+                + mAdEventType
+                + ", mCallerAdTech="
+                + mCallerAdTech
+                + '}';
+    }
+
+    /** Builder for {@link UpdateAdCounterHistogramRequest} objects. */
+    public static final class Builder {
+        private long mAdSelectionId;
+        @FrequencyCapFilters.AdEventType private int mAdEventType;
+        @NonNull private AdTechIdentifier mCallerAdTech;
+
+        public Builder(
+                long adSelectionId, int adEventType, @NonNull AdTechIdentifier callerAdTech) {
+            Preconditions.checkArgument(
+                    adSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+            Preconditions.checkArgument(
+                    adEventType != AD_EVENT_TYPE_WIN, DISALLOW_AD_EVENT_TYPE_WIN_MESSAGE);
+            Preconditions.checkArgument(
+                    adEventType >= FrequencyCapFilters.AD_EVENT_TYPE_MIN
+                            && adEventType <= FrequencyCapFilters.AD_EVENT_TYPE_MAX,
+                    INVALID_AD_EVENT_TYPE_MESSAGE);
+            Objects.requireNonNull(callerAdTech, UNSET_CALLER_ADTECH_MESSAGE);
+
+            mAdSelectionId = adSelectionId;
+            mAdEventType = adEventType;
+            mCallerAdTech = callerAdTech;
+        }
+
+        /**
+         * Sets the ad selection ID with which the rendered ad's events are associated.
+         *
+         * <p>See {@link #getAdSelectionId()} for more information.
+         */
+        @NonNull
+        public Builder setAdSelectionId(long adSelectionId) {
+            Preconditions.checkArgument(
+                    adSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
+            mAdSelectionId = adSelectionId;
+            return this;
+        }
+
+        /**
+         * Sets the ad event type which, along with an ad's counter keys, identifies which histogram
+         * should be updated.
+         *
+         * <p>See {@link #getAdEventType()} for more information.
+         */
+        @NonNull
+        public Builder setAdEventType(@FrequencyCapFilters.AdEventType int adEventType) {
+            Preconditions.checkArgument(
+                    adEventType != AD_EVENT_TYPE_WIN, DISALLOW_AD_EVENT_TYPE_WIN_MESSAGE);
+            Preconditions.checkArgument(
+                    adEventType >= FrequencyCapFilters.AD_EVENT_TYPE_MIN
+                            && adEventType <= FrequencyCapFilters.AD_EVENT_TYPE_MAX,
+                    INVALID_AD_EVENT_TYPE_MESSAGE);
+            mAdEventType = adEventType;
+            return this;
+        }
+
+        /**
+         * Sets the caller adtech entity's {@link AdTechIdentifier}.
+         *
+         * <p>See {@link #getCallerAdTech()} for more information.
+         */
+        @NonNull
+        public Builder setCallerAdTech(@NonNull AdTechIdentifier callerAdTech) {
+            Objects.requireNonNull(callerAdTech, UNSET_CALLER_ADTECH_MESSAGE);
+            mCallerAdTech = callerAdTech;
+            return this;
+        }
+
+        /** Builds the {@link UpdateAdCounterHistogramRequest} object. */
+        @NonNull
+        public UpdateAdCounterHistogramRequest build() {
+            return new UpdateAdCounterHistogramRequest(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/appsetid/AppSetId.java b/android-35/android/adservices/appsetid/AppSetId.java
new file mode 100644
index 0000000..247ad6a
--- /dev/null
+++ b/android-35/android/adservices/appsetid/AppSetId.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.appsetid;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A unique, per-device, per developer-account user-resettable ID for non-monetizing advertising
+ * usecases.
+ *
+ * <p>Represents the appSetID and scope of this appSetId from the {@link
+ * AppSetIdManager#getAppSetId(Executor, OutcomeReceiver)} API. The scope of the ID can be per app
+ * or per developer account associated with the user. AppSetId is used for analytics, spam
+ * detection, frequency capping and fraud prevention use cases, on a given device, that one may need
+ * to correlate usage or actions across a set of apps owned by an organization.
+ */
+public class AppSetId {
+    @NonNull private final String mAppSetId;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+        SCOPE_APP,
+        SCOPE_DEVELOPER,
+    })
+    public @interface AppSetIdScope {}
+    /** The appSetId is scoped to an app. All apps on a device will have a different appSetId. */
+    public static final int SCOPE_APP = 1;
+
+    /**
+     * The appSetId is scoped to a developer account on an app store. All apps from the same
+     * developer on a device will have the same developer scoped appSetId.
+     */
+    public static final int SCOPE_DEVELOPER = 2;
+
+    private final @AppSetIdScope int mAppSetIdScope;
+
+    /**
+     * Creates an instance of {@link AppSetId}
+     *
+     * @param appSetId generated by the provider service.
+     * @param appSetIdScope scope of the appSetId.
+     */
+    public AppSetId(@NonNull String appSetId, @AppSetIdScope int appSetIdScope) {
+        mAppSetId = appSetId;
+        mAppSetIdScope = appSetIdScope;
+    }
+
+    /** Retrieves the appSetId. The api always returns a non-empty appSetId. */
+    public @NonNull String getId() {
+        return mAppSetId;
+    }
+
+    /** Retrieves the scope of the appSetId. */
+    public @AppSetIdScope int getScope() {
+        return mAppSetIdScope;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof AppSetId)) {
+            return false;
+        }
+        AppSetId that = (AppSetId) o;
+        return mAppSetId.equals(that.mAppSetId) && (mAppSetIdScope == that.mAppSetIdScope);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAppSetId, mAppSetIdScope);
+    }
+}
diff --git a/android-35/android/adservices/appsetid/AppSetIdManager.java b/android-35/android/adservices/appsetid/AppSetIdManager.java
new file mode 100644
index 0000000..1516356
--- /dev/null
+++ b/android-35/android/adservices/appsetid/AppSetIdManager.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.appsetid;
+
+import android.adservices.common.AdServicesStatusUtils;
+import android.adservices.common.CallerMetadata;
+import android.adservices.common.SandboxedSdkContextUtils;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.app.sdksandbox.SandboxedSdkContext;
+import android.content.Context;
+import android.os.Build;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+import android.os.SystemClock;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.adservices.AdServicesCommon;
+import com.android.adservices.LogUtil;
+import com.android.adservices.ServiceBinder;
+import com.android.adservices.shared.common.ServiceUnavailableException;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * AppSetIdManager provides APIs for app and ad-SDKs to access appSetId for non-monetizing purpose.
+ */
+@RequiresApi(Build.VERSION_CODES.S)
+public class AppSetIdManager {
+    /**
+     * Service used for registering AppSetIdManager in the system service registry.
+     *
+     * @hide
+     */
+    public static final String APPSETID_SERVICE = "appsetid_service";
+
+    /* When an app calls the AppSetId API directly, it sets the SDK name to empty string. */
+    static final String EMPTY_SDK = "";
+
+    private Context mContext;
+    private ServiceBinder<IAppSetIdService> mServiceBinder;
+
+    /**
+     * Factory method for creating an instance of AppSetIdManager.
+     *
+     * @param context The {@link Context} to use
+     * @return A {@link AppSetIdManager} instance
+     */
+    @NonNull
+    public static AppSetIdManager get(@NonNull Context context) {
+        // On T+, context.getSystemService() does more than just call constructor.
+        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+                ? context.getSystemService(AppSetIdManager.class)
+                : new AppSetIdManager(context);
+    }
+
+    /**
+     * Create AppSetIdManager
+     *
+     * @hide
+     */
+    public AppSetIdManager(Context context) {
+        // In case the AppSetIdManager is initiated from inside a sdk_sandbox process the fields
+        // will be immediately rewritten by the initialize method below.
+        initialize(context);
+    }
+
+    /**
+     * Initializes {@link AppSetIdManager} with the given {@code context}.
+     *
+     * <p>This method is called by the {@link SandboxedSdkContext} to propagate the correct context.
+     * For more information check the javadoc on the {@link
+     * android.app.sdksandbox.SdkSandboxSystemServiceRegistry}.
+     *
+     * @hide
+     * @see android.app.sdksandbox.SdkSandboxSystemServiceRegistry
+     */
+    public AppSetIdManager initialize(Context context) {
+        mContext = context;
+        mServiceBinder =
+                ServiceBinder.getServiceBinder(
+                        context,
+                        AdServicesCommon.ACTION_APPSETID_SERVICE,
+                        IAppSetIdService.Stub::asInterface);
+        return this;
+    }
+
+    @NonNull
+    private IAppSetIdService getService(
+            @CallbackExecutor Executor executor, OutcomeReceiver<AppSetId, Exception> callback) {
+        IAppSetIdService service = null;
+        try {
+            service = mServiceBinder.getService();
+
+            // Throw ServiceUnavailableException and set it to the callback.
+            if (service == null) {
+                throw new ServiceUnavailableException();
+            }
+        } catch (RuntimeException e) {
+            LogUtil.e(e, "Failed binding to AppSetId service");
+            executor.execute(() -> callback.onError(e));
+        }
+
+        return service;
+    }
+
+    @NonNull
+    private Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * Retrieve the AppSetId.
+     *
+     * @param executor The executor to run callback.
+     * @param callback The callback that's called after appsetid are available or an error occurs.
+     */
+    @NonNull
+    public void getAppSetId(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<AppSetId, Exception> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+        CallerMetadata callerMetadata =
+                new CallerMetadata.Builder()
+                        .setBinderElapsedTimestamp(SystemClock.elapsedRealtime())
+                        .build();
+
+        String appPackageName = "";
+        String sdkPackageName = "";
+        // First check if context is SandboxedSdkContext or not
+        Context getAppSetIdRequestContext = getContext();
+        SandboxedSdkContext requestContext =
+                SandboxedSdkContextUtils.getAsSandboxedSdkContext(getAppSetIdRequestContext);
+        if (requestContext != null) {
+            sdkPackageName = requestContext.getSdkPackageName();
+            appPackageName = requestContext.getClientPackageName();
+        } else { // This is the case without the Sandbox.
+            appPackageName = getAppSetIdRequestContext.getPackageName();
+        }
+        try {
+            IAppSetIdService service = getService(executor, callback);
+            if (service == null) {
+                LogUtil.d("Unable to find AppSetId service");
+                return;
+            }
+
+            service.getAppSetId(
+                    new GetAppSetIdParam.Builder()
+                            .setAppPackageName(appPackageName)
+                            .setSdkPackageName(sdkPackageName)
+                            .build(),
+                    callerMetadata,
+                    new IGetAppSetIdCallback.Stub() {
+                        @Override
+                        public void onResult(GetAppSetIdResult resultParcel) {
+                            executor.execute(
+                                    () -> {
+                                        if (resultParcel.isSuccess()) {
+                                            callback.onResult(
+                                                    new AppSetId(
+                                                            resultParcel.getAppSetId(),
+                                                            resultParcel.getAppSetIdScope()));
+                                        } else {
+                                            callback.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            resultParcel));
+                                        }
+                                    });
+                        }
+
+                        @Override
+                        public void onError(int resultCode) {
+                            executor.execute(
+                                    () ->
+                                            callback.onError(
+                                                    AdServicesStatusUtils.asException(resultCode)));
+                        }
+                    });
+        } catch (RemoteException e) {
+            LogUtil.e("RemoteException", e);
+            callback.onError(e);
+        }
+    }
+
+    /**
+     * If the service is in an APK (as opposed to the system service), unbind it from the service to
+     * allow the APK process to die.
+     *
+     * @hide
+     */
+    // TODO: change to @VisibleForTesting
+    public void unbindFromService() {
+        mServiceBinder.unbindFromService();
+    }
+}
diff --git a/android-35/android/adservices/appsetid/AppSetIdProviderService.java b/android-35/android/adservices/appsetid/AppSetIdProviderService.java
new file mode 100644
index 0000000..fbe93fa
--- /dev/null
+++ b/android-35/android/adservices/appsetid/AppSetIdProviderService.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.appsetid;
+
+import static android.adservices.common.AdServicesStatusUtils.STATUS_SUCCESS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import java.io.IOException;
+
+/**
+ * Abstract Base class for provider service to implement generation of AppSetId with appropriate
+ * appSetId scope value.
+ *
+ * <p>The implementor of this service needs to override the onGetAppSetIdProvider method and provide
+ * an app-scoped or developer-account scoped unique appSetId.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class AppSetIdProviderService extends Service {
+
+    /** The intent that the service must respond to. Add it to the intent filter of the service. */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.adservices.appsetid.AppSetIdProviderService";
+
+    /** Abstract method which will be overridden by provider to provide the appsetid. */
+    @NonNull
+    public abstract AppSetId onGetAppSetId(int clientUid, @NonNull String clientPackageName)
+            throws IOException;
+
+    private final android.adservices.appsetid.IAppSetIdProviderService mInterface =
+            new android.adservices.appsetid.IAppSetIdProviderService.Stub() {
+                @Override
+                public void getAppSetId(
+                        int appUID,
+                        @NonNull String packageName,
+                        @NonNull IGetAppSetIdProviderCallback resultCallback)
+                        throws RemoteException {
+                    try {
+                        AppSetId appsetId = onGetAppSetId(appUID, packageName);
+                        GetAppSetIdResult appsetIdInternal =
+                                new GetAppSetIdResult.Builder()
+                                        .setStatusCode(STATUS_SUCCESS)
+                                        .setErrorMessage("")
+                                        .setAppSetId(appsetId.getId())
+                                        .setAppSetIdScope(appsetId.getScope())
+                                        .build();
+
+                        resultCallback.onResult(appsetIdInternal);
+                    } catch (Throwable e) {
+                        resultCallback.onError(e.getMessage());
+                    }
+                }
+            };
+
+    @Nullable
+    @Override
+    public final IBinder onBind(@Nullable Intent intent) {
+        return mInterface.asBinder();
+    }
+}
diff --git a/android-35/android/adservices/appsetid/GetAppSetIdParam.java b/android-35/android/adservices/appsetid/GetAppSetIdParam.java
new file mode 100644
index 0000000..af54f52
--- /dev/null
+++ b/android-35/android/adservices/appsetid/GetAppSetIdParam.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.appsetid;
+
+import static android.adservices.appsetid.AppSetIdManager.EMPTY_SDK;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represent input params to the getAppSetId API.
+ *
+ * @hide
+ */
+public final class GetAppSetIdParam implements Parcelable {
+    private final String mSdkPackageName;
+    private final String mAppPackageName;
+
+    private GetAppSetIdParam(@Nullable String sdkPackageName, @NonNull String appPackageName) {
+        mSdkPackageName = sdkPackageName;
+        mAppPackageName = appPackageName;
+    }
+
+    private GetAppSetIdParam(@NonNull Parcel in) {
+        mSdkPackageName = in.readString();
+        mAppPackageName = in.readString();
+    }
+
+    public static final @NonNull Creator<GetAppSetIdParam> CREATOR =
+            new Parcelable.Creator<GetAppSetIdParam>() {
+                @Override
+                public GetAppSetIdParam createFromParcel(Parcel in) {
+                    return new GetAppSetIdParam(in);
+                }
+
+                @Override
+                public GetAppSetIdParam[] newArray(int size) {
+                    return new GetAppSetIdParam[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeString(mSdkPackageName);
+        out.writeString(mAppPackageName);
+    }
+
+    /** Get the Sdk Package Name. This is the package name in the Manifest. */
+    @NonNull
+    public String getSdkPackageName() {
+        return mSdkPackageName;
+    }
+
+    /** Get the App PackageName. */
+    @NonNull
+    public String getAppPackageName() {
+        return mAppPackageName;
+    }
+
+    /** Builder for {@link GetAppSetIdParam} objects. */
+    public static final class Builder {
+        private String mSdkPackageName;
+        private String mAppPackageName;
+
+        public Builder() {}
+
+        /**
+         * Set the Sdk Package Name. When the app calls the AppSetId API directly without using an
+         * SDK, don't set this field.
+         */
+        public @NonNull Builder setSdkPackageName(@NonNull String sdkPackageName) {
+            mSdkPackageName = sdkPackageName;
+            return this;
+        }
+
+        /** Set the App PackageName. */
+        public @NonNull Builder setAppPackageName(@NonNull String appPackageName) {
+            mAppPackageName = appPackageName;
+            return this;
+        }
+
+        /** Builds a {@link GetAppSetIdParam} instance. */
+        public @NonNull GetAppSetIdParam build() {
+            if (mSdkPackageName == null) {
+                // When Sdk package name is not set, we assume the App calls the AppSetId API
+                // directly.
+                // We set the Sdk package name to empty to mark this.
+                mSdkPackageName = EMPTY_SDK;
+            }
+
+            if (mAppPackageName == null || mAppPackageName.isEmpty()) {
+                throw new IllegalArgumentException("App PackageName must not be empty or null");
+            }
+
+            return new GetAppSetIdParam(mSdkPackageName, mAppPackageName);
+        }
+    }
+}
diff --git a/android-35/android/adservices/appsetid/GetAppSetIdResult.java b/android-35/android/adservices/appsetid/GetAppSetIdResult.java
new file mode 100644
index 0000000..9a750a6
--- /dev/null
+++ b/android-35/android/adservices/appsetid/GetAppSetIdResult.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.appsetid;
+
+import android.adservices.common.AdServicesResponse;
+import android.adservices.common.AdServicesStatusUtils;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Represent the result from the getAppSetId API.
+ *
+ * @hide
+ */
+public final class GetAppSetIdResult extends AdServicesResponse {
+    @NonNull private final String mAppSetId;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+        SCOPE_APP,
+        SCOPE_DEVELOPER,
+    })
+    public @interface AppSetIdScope {}
+    /** The appSetId is scoped to an app. All apps on a device will have a different appSetId. */
+    public static final int SCOPE_APP = 1;
+
+    /**
+     * The appSetId is scoped to a developer account on an app store. All apps from the same
+     * developer on a device will have the same developer scoped appSetId.
+     */
+    public static final int SCOPE_DEVELOPER = 2;
+
+    private final @AppSetIdScope int mAppSetIdScope;
+
+    private GetAppSetIdResult(
+            @AdServicesStatusUtils.StatusCode int resultCode,
+            @Nullable String errorMessage,
+            @NonNull String appSetId,
+            @AppSetIdScope int appSetIdScope) {
+        super(resultCode, errorMessage);
+        mAppSetId = appSetId;
+        mAppSetIdScope = appSetIdScope;
+    }
+
+    private GetAppSetIdResult(@NonNull Parcel in) {
+        super(in);
+        Objects.requireNonNull(in);
+
+        mAppSetId = in.readString();
+        mAppSetIdScope = in.readInt();
+    }
+
+    public static final @NonNull Creator<GetAppSetIdResult> CREATOR =
+            new Parcelable.Creator<GetAppSetIdResult>() {
+                @Override
+                public GetAppSetIdResult createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new GetAppSetIdResult(in);
+                }
+
+                @Override
+                public GetAppSetIdResult[] newArray(int size) {
+                    return new GetAppSetIdResult[size];
+                }
+            };
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mStatusCode);
+        out.writeString(mErrorMessage);
+        out.writeString(mAppSetId);
+        out.writeInt(mAppSetIdScope);
+    }
+
+    /**
+     * Returns the error message associated with this result.
+     *
+     * <p>If {@link #isSuccess} is {@code true}, the error message is always {@code null}. The error
+     * message may be {@code null} even if {@link #isSuccess} is {@code false}.
+     */
+    @Nullable
+    public String getErrorMessage() {
+        return mErrorMessage;
+    }
+
+    /** Returns the AppSetId associated with this result. */
+    @NonNull
+    public String getAppSetId() {
+        return mAppSetId;
+    }
+
+    /** Returns the AppSetId scope associated with this result. */
+    public @AppSetIdScope int getAppSetIdScope() {
+        return mAppSetIdScope;
+    }
+
+    @Override
+    public String toString() {
+        return "GetAppSetIdResult{"
+                + "mResultCode="
+                + mStatusCode
+                + ", mErrorMessage='"
+                + mErrorMessage
+                + '\''
+                + ", mAppSetId="
+                + mAppSetId
+                + ", mAppSetIdScope="
+                + mAppSetIdScope
+                + '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof GetAppSetIdResult)) {
+            return false;
+        }
+
+        GetAppSetIdResult that = (GetAppSetIdResult) o;
+
+        return mStatusCode == that.mStatusCode
+                && Objects.equals(mErrorMessage, that.mErrorMessage)
+                && Objects.equals(mAppSetId, that.mAppSetId)
+                && (mAppSetIdScope == that.mAppSetIdScope);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mStatusCode, mErrorMessage, mAppSetId, mAppSetIdScope);
+    }
+
+    /**
+     * Builder for {@link GetAppSetIdResult} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private @AdServicesStatusUtils.StatusCode int mStatusCode;
+        @Nullable private String mErrorMessage;
+        @NonNull private String mAppSetId;
+        private @AppSetIdScope int mAppSetIdScope;
+
+        public Builder() {}
+
+        /** Set the Result Code. */
+        public @NonNull Builder setStatusCode(@AdServicesStatusUtils.StatusCode int statusCode) {
+            mStatusCode = statusCode;
+            return this;
+        }
+
+        /** Set the Error Message. */
+        public @NonNull Builder setErrorMessage(@Nullable String errorMessage) {
+            mErrorMessage = errorMessage;
+            return this;
+        }
+
+        /** Set the appSetId. */
+        public @NonNull Builder setAppSetId(@NonNull String appSetId) {
+            mAppSetId = appSetId;
+            return this;
+        }
+
+        /** Set the appSetId scope field. */
+        public @NonNull Builder setAppSetIdScope(@AppSetIdScope int scope) {
+            mAppSetIdScope = scope;
+            return this;
+        }
+
+        /** Builds a {@link GetAppSetIdResult} instance. */
+        public @NonNull GetAppSetIdResult build() {
+            if (mAppSetId == null) {
+                throw new IllegalArgumentException("appSetId is null");
+            }
+
+            return new GetAppSetIdResult(mStatusCode, mErrorMessage, mAppSetId, mAppSetIdScope);
+        }
+    }
+}
diff --git a/android-35/android/adservices/cobalt/AdServicesCobaltUploadService.java b/android-35/android/adservices/cobalt/AdServicesCobaltUploadService.java
new file mode 100644
index 0000000..cfb8787
--- /dev/null
+++ b/android-35/android/adservices/cobalt/AdServicesCobaltUploadService.java
@@ -0,0 +1,68 @@
+/*
+ * 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 android.adservices.cobalt;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * Abstract Base class for provider service to implement uploading of data to Cobalt's backend.
+ *
+ * <p>The implementor of this service needs to override the onUploadEncryptedCobaltEnvelope method.
+ *
+ * <p>Cobalt is a telemetry system with built-in support for differential privacy. See
+ * https://fuchsia.googlesource.com/cobalt for a comprehensive overview of the project and the
+ * Fuchsia client implementation.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class AdServicesCobaltUploadService extends Service {
+    /** The intent that the service must respond to. Add it to the intent filter of the service. */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.adservices.cobalt.AdServicesCobaltUploadService";
+
+    /** Abstract method which will be overridden by the sender to upload the data */
+    public abstract void onUploadEncryptedCobaltEnvelope(
+            @NonNull EncryptedCobaltEnvelopeParams params);
+
+    private final IAdServicesCobaltUploadService mInterface =
+            new IAdServicesCobaltUploadService.Stub() {
+                /**
+                 * Send an encrypted envelope to Cobalt's backend.
+                 *
+                 * <p>Errors in this method execution, both because of problems within the binder
+                 * call and in the service execution, will cause a RuntimeException to be thrown.
+                 */
+                @Override
+                public void uploadEncryptedCobaltEnvelope(EncryptedCobaltEnvelopeParams params) {
+                    onUploadEncryptedCobaltEnvelope(params);
+                }
+            };
+
+    @Nullable
+    @Override
+    public final IBinder onBind(@Nullable Intent intent) {
+        return mInterface.asBinder();
+    }
+}
diff --git a/android-35/android/adservices/cobalt/EncryptedCobaltEnvelopeParams.java b/android-35/android/adservices/cobalt/EncryptedCobaltEnvelopeParams.java
new file mode 100644
index 0000000..0e985e1
--- /dev/null
+++ b/android-35/android/adservices/cobalt/EncryptedCobaltEnvelopeParams.java
@@ -0,0 +1,132 @@
+/*
+ * 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 android.adservices.cobalt;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Parameters describing the encrypted Cobalt Envelope being sent.
+ *
+ * @hide
+ */
+@SystemApi
+public final class EncryptedCobaltEnvelopeParams implements Parcelable {
+    /**
+     * Whether data is from a development or production device.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+        ENVIRONMENT_DEV,
+        ENVIRONMENT_PROD,
+    })
+    public @interface Environment {}
+
+    /** Production environment. */
+    public static final int ENVIRONMENT_PROD = 0;
+
+    /** Development environment. */
+    public static final int ENVIRONMENT_DEV = 1;
+
+    private final @Environment int mEnvironment;
+    private final int mKeyIndex;
+    private final byte[] mCipherText;
+
+    /**
+     * The parameters describing how a Cobalt {@link Envelope} was encrypted and the ciphertext.
+     *
+     * @param environment the environment the {@link Envelope} was encrypted for
+     * @param keyIndex the identifier of the key used for encryption, see
+     *     //packages/modules/AdServices/adservices/libraries/cobalt/java/com/android/cobalt/crypto/PublicKeys.java
+     *     for key list
+     * @param cipherText an encrypted Cobalt {@link Envelope}, created using a supported encryption
+     *     algorithm and an associated key.
+     */
+    public EncryptedCobaltEnvelopeParams(
+            @Environment int environment, @NonNull int keyIndex, @NonNull byte[] cipherText) {
+        mEnvironment = environment;
+        mKeyIndex = keyIndex;
+        mCipherText = Objects.requireNonNull(cipherText);
+    }
+
+    private EncryptedCobaltEnvelopeParams(@NonNull Parcel in) {
+        mEnvironment = in.readInt();
+        mKeyIndex = in.readInt();
+        mCipherText = in.createByteArray();
+    }
+
+    public static final @NonNull Creator<EncryptedCobaltEnvelopeParams> CREATOR =
+            new Parcelable.Creator<EncryptedCobaltEnvelopeParams>() {
+                @Override
+                public EncryptedCobaltEnvelopeParams createFromParcel(Parcel in) {
+                    return new EncryptedCobaltEnvelopeParams(in);
+                }
+
+                @Override
+                public EncryptedCobaltEnvelopeParams[] newArray(int size) {
+                    return new EncryptedCobaltEnvelopeParams[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mEnvironment);
+        out.writeInt(mKeyIndex);
+        out.writeByteArray(mCipherText);
+    }
+
+    /** Get the environment. */
+    @NonNull
+    public @Environment int getEnvironment() {
+        return mEnvironment;
+    }
+
+    /**
+     * Get index of the (public, private) key pair used to encrypted the Envelope.
+     *
+     * <p>There are multiple pairs on the server and it's cheaper to send the index than the actual
+     * public key used.
+     */
+    @NonNull
+    public int getKeyIndex() {
+        return mKeyIndex;
+    }
+
+    /**
+     * Get the encrypted Envelope.
+     *
+     * <p>Envelopes are will be on the order of 1KiB in size.
+     */
+    @NonNull
+    public byte[] getCipherText() {
+        return mCipherText;
+    }
+}
diff --git a/android-35/android/adservices/common/AdData.java b/android-35/android/adservices/common/AdData.java
new file mode 100644
index 0000000..f23d03a
--- /dev/null
+++ b/android-35/android/adservices/common/AdData.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.common;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.AdServicesParcelableUtil;
+import com.android.internal.util.Preconditions;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/** Represents data specific to an ad that is necessary for ad selection and rendering. */
+public final class AdData implements Parcelable {
+    /** @hide */
+    public static final String NUM_AD_COUNTER_KEYS_EXCEEDED_FORMAT =
+            "AdData should have no more than %d ad counter keys";
+    /** @hide */
+    public static final int MAX_NUM_AD_COUNTER_KEYS = 10;
+
+    @NonNull private final Uri mRenderUri;
+    @NonNull private final String mMetadata;
+    @NonNull private final Set<Integer> mAdCounterKeys;
+    @Nullable private final AdFilters mAdFilters;
+    @Nullable private final String mAdRenderId;
+
+    @NonNull
+    public static final Creator<AdData> CREATOR =
+            new Creator<AdData>() {
+                @Override
+                public AdData createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+
+                    return new AdData(in);
+                }
+
+                @Override
+                public AdData[] newArray(int size) {
+                    return new AdData[size];
+                }
+            };
+
+    private AdData(@NonNull AdData.Builder builder) {
+        Objects.requireNonNull(builder);
+
+        mRenderUri = builder.mRenderUri;
+        mMetadata = builder.mMetadata;
+        mAdCounterKeys = builder.mAdCounterKeys;
+        mAdFilters = builder.mAdFilters;
+        mAdRenderId = builder.mAdRenderId;
+    }
+
+    private AdData(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        mRenderUri = Uri.CREATOR.createFromParcel(in);
+        mMetadata = in.readString();
+        mAdCounterKeys =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, AdServicesParcelableUtil::readIntegerSetFromParcel);
+        mAdFilters =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, AdFilters.CREATOR::createFromParcel);
+        mAdRenderId = in.readString();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        mRenderUri.writeToParcel(dest, flags);
+        dest.writeString(mMetadata);
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest, mAdCounterKeys, AdServicesParcelableUtil::writeIntegerSetToParcel);
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mAdFilters,
+                (targetParcel, sourceFilters) -> sourceFilters.writeToParcel(targetParcel, flags));
+        dest.writeString(mAdRenderId);
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Gets the URI that points to the ad's rendering assets. The URI must use HTTPS. */
+    @NonNull
+    public Uri getRenderUri() {
+        return mRenderUri;
+    }
+
+    /**
+     * Gets the buyer ad metadata used during the ad selection process.
+     *
+     * <p>The metadata should be a valid JSON object serialized as a string. Metadata represents
+     * ad-specific bidding information that will be used during ad selection as part of bid
+     * generation and used in buyer JavaScript logic, which is executed in an isolated execution
+     * environment.
+     *
+     * <p>If the metadata is not a valid JSON object that can be consumed by the buyer's JS, the ad
+     * will not be eligible for ad selection.
+     */
+    @NonNull
+    public String getMetadata() {
+        return mMetadata;
+    }
+
+    /**
+     * Gets the set of keys used in counting events.
+     *
+     * <p>No more than 10 ad counter keys may be associated with an ad.
+     *
+     * <p>The keys and counts per key are used in frequency cap filtering during ad selection to
+     * disqualify associated ads from being submitted to bidding.
+     *
+     * <p>Note that these keys can be overwritten along with the ads and other bidding data for a
+     * custom audience during the custom audience's daily update.
+     */
+    @NonNull
+    public Set<Integer> getAdCounterKeys() {
+        return mAdCounterKeys;
+    }
+
+    /**
+     * Gets all {@link AdFilters} associated with the ad.
+     *
+     * <p>The filters, if met or exceeded, exclude the associated ad from participating in ad
+     * selection. They are optional and if {@code null} specify that no filters apply to this ad.
+     */
+    @Nullable
+    public AdFilters getAdFilters() {
+        return mAdFilters;
+    }
+
+    /**
+     * Gets the ad render id for server auctions.
+     *
+     * <p>Ad render id is collected for each {@link AdData} when server auction request is received.
+     *
+     * <p>Any {@link AdData} without ad render id will be ineligible for server-side auction.
+     *
+     * <p>The overall size of the CA is limited. The size of this field is considered using
+     * {@link String#getBytes()} in {@code UTF-8} encoding.
+     */
+    @Nullable
+    public String getAdRenderId() {
+        return mAdRenderId;
+    }
+
+    /** Checks whether two {@link AdData} objects contain the same information. */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AdData)) return false;
+        AdData adData = (AdData) o;
+        return mRenderUri.equals(adData.mRenderUri)
+                && mMetadata.equals(adData.mMetadata)
+                && mAdCounterKeys.equals(adData.mAdCounterKeys)
+                && Objects.equals(mAdFilters, adData.mAdFilters)
+                && Objects.equals(mAdRenderId, adData.mAdRenderId);
+    }
+
+    /** Returns the hash of the {@link AdData} object's data. */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRenderUri, mMetadata, mAdCounterKeys, mAdFilters);
+    }
+
+    @Override
+    public String toString() {
+        return "AdData{"
+                + "mRenderUri="
+                + mRenderUri
+                + ", mMetadata='"
+                + mMetadata
+                + '\''
+                + ", mAdCounterKeys="
+                + mAdCounterKeys
+                + ", mAdFilters="
+                + mAdFilters
+                + ", mAdRenderId='"
+                + mAdRenderId
+                + '\''
+                + '}';
+    }
+
+    /** Builder for {@link AdData} objects. */
+    public static final class Builder {
+        @Nullable private Uri mRenderUri;
+        @Nullable private String mMetadata;
+        @NonNull private Set<Integer> mAdCounterKeys = new HashSet<>();
+        @Nullable private AdFilters mAdFilters;
+        @Nullable private String mAdRenderId;
+
+        // TODO(b/232883403): We may need to add @NonNUll members as args.
+        public Builder() {}
+
+        /**
+         * Sets the URI that points to the ad's rendering assets. The URI must use HTTPS.
+         *
+         * <p>See {@link #getRenderUri()} for detail.
+         */
+        @NonNull
+        public AdData.Builder setRenderUri(@NonNull Uri renderUri) {
+            Objects.requireNonNull(renderUri);
+            mRenderUri = renderUri;
+            return this;
+        }
+
+        /**
+         * Sets the buyer ad metadata used during the ad selection process.
+         *
+         * <p>The metadata should be a valid JSON object serialized as a string. Metadata represents
+         * ad-specific bidding information that will be used during ad selection as part of bid
+         * generation and used in buyer JavaScript logic, which is executed in an isolated execution
+         * environment.
+         *
+         * <p>If the metadata is not a valid JSON object that can be consumed by the buyer's JS, the
+         * ad will not be eligible for ad selection.
+         *
+         * <p>See {@link #getMetadata()} for detail.
+         */
+        @NonNull
+        public AdData.Builder setMetadata(@NonNull String metadata) {
+            Objects.requireNonNull(metadata);
+            mMetadata = metadata;
+            return this;
+        }
+
+        /**
+         * Sets the set of keys used in counting events.
+         *
+         * <p>No more than 10 ad counter keys may be associated with an ad.
+         *
+         * <p>See {@link #getAdCounterKeys()} for more information.
+         */
+        @NonNull
+        public AdData.Builder setAdCounterKeys(@NonNull Set<Integer> adCounterKeys) {
+            Objects.requireNonNull(adCounterKeys);
+            Preconditions.checkArgument(
+                    !adCounterKeys.contains(null), "Ad counter keys must not contain null value");
+            Preconditions.checkArgument(
+                    adCounterKeys.size() <= MAX_NUM_AD_COUNTER_KEYS,
+                    NUM_AD_COUNTER_KEYS_EXCEEDED_FORMAT,
+                    MAX_NUM_AD_COUNTER_KEYS);
+            mAdCounterKeys = adCounterKeys;
+            return this;
+        }
+
+        /**
+         * Sets all {@link AdFilters} associated with the ad.
+         *
+         * <p>See {@link #getAdFilters()} for more information.
+         */
+        @NonNull
+        public AdData.Builder setAdFilters(@Nullable AdFilters adFilters) {
+            mAdFilters = adFilters;
+            return this;
+        }
+
+        /**
+         * Sets the ad render id for server auction
+         *
+         * <p>See {@link AdData#getAdRenderId()} for more information.
+         */
+        @NonNull
+        public AdData.Builder setAdRenderId(@Nullable String adRenderId) {
+            mAdRenderId = adRenderId;
+            return this;
+        }
+
+        /**
+         * Builds the {@link AdData} object.
+         *
+         * @throws NullPointerException if any required parameters are {@code null} when built
+         */
+        @NonNull
+        public AdData build() {
+            Objects.requireNonNull(mRenderUri, "The render URI has not been provided");
+            // TODO(b/231997523): Add JSON field validation.
+            Objects.requireNonNull(mMetadata, "The metadata has not been provided");
+
+            return new AdData(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/common/AdFilters.java b/android-35/android/adservices/common/AdFilters.java
new file mode 100644
index 0000000..0d1b1f8
--- /dev/null
+++ b/android-35/android/adservices/common/AdFilters.java
@@ -0,0 +1,243 @@
+/*
+ * 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 android.adservices.common;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.AdServicesParcelableUtil;
+import com.android.adservices.flags.Flags;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Objects;
+
+/**
+ * A container class for filters which are associated with an ad.
+ *
+ * <p>If any of the filters in an {@link AdFilters} instance are not satisfied, the associated ad
+ * will not be eligible for ad selection. Filters are optional ad parameters and are not required as
+ * part of {@link AdData}.
+ */
+public final class AdFilters implements Parcelable {
+    /** @hide */
+    public static final String FREQUENCY_CAP_FIELD_NAME = "frequency_cap";
+    /** @hide */
+    public static final String APP_INSTALL_FIELD_NAME = "app_install";
+    /** @hide */
+    @Nullable private final FrequencyCapFilters mFrequencyCapFilters;
+
+    @Nullable private final AppInstallFilters mAppInstallFilters;
+
+    @NonNull
+    public static final Creator<AdFilters> CREATOR =
+            new Creator<AdFilters>() {
+                @Override
+                public AdFilters createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new AdFilters(in);
+                }
+
+                @Override
+                public AdFilters[] newArray(int size) {
+                    return new AdFilters[size];
+                }
+            };
+
+    private AdFilters(@NonNull Builder builder) {
+        Objects.requireNonNull(builder);
+
+        mFrequencyCapFilters = builder.mFrequencyCapFilters;
+        mAppInstallFilters = builder.mAppInstallFilters;
+    }
+
+    private AdFilters(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        mFrequencyCapFilters =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, FrequencyCapFilters.CREATOR::createFromParcel);
+        mAppInstallFilters =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, AppInstallFilters.CREATOR::createFromParcel);
+    }
+
+    /**
+     * Gets the {@link FrequencyCapFilters} instance that represents all frequency cap filters for
+     * the ad.
+     *
+     * <p>If {@code null}, there are no frequency cap filters which apply to the ad.
+     */
+    @Nullable
+    public FrequencyCapFilters getFrequencyCapFilters() {
+        return mFrequencyCapFilters;
+    }
+
+    /**
+     * Gets the {@link AppInstallFilters} instance that represents all app install filters for the
+     * ad.
+     *
+     * <p>If {@code null}, there are no app install filters which apply to the ad.
+     */
+    @FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
+    @Nullable
+    public AppInstallFilters getAppInstallFilters() {
+        return mAppInstallFilters;
+    }
+
+    /**
+     * @return The estimated size of this object, in bytes.
+     * @hide
+     */
+    public int getSizeInBytes() {
+        int size = 0;
+        if (mFrequencyCapFilters != null) {
+            size += mFrequencyCapFilters.getSizeInBytes();
+        }
+        if (mAppInstallFilters != null) {
+            size += mAppInstallFilters.getSizeInBytes();
+        }
+        return size;
+    }
+
+    /**
+     * A JSON serializer.
+     *
+     * @return A JSON serialization of this object.
+     * @hide
+     */
+    public JSONObject toJson() throws JSONException {
+        JSONObject toReturn = new JSONObject();
+        if (mFrequencyCapFilters != null) {
+            toReturn.put(FREQUENCY_CAP_FIELD_NAME, mFrequencyCapFilters.toJson());
+        }
+        if (mAppInstallFilters != null) {
+            toReturn.put(APP_INSTALL_FIELD_NAME, mAppInstallFilters.toJson());
+        }
+        return toReturn;
+    }
+
+    /**
+     * A JSON de-serializer.
+     *
+     * @param json A JSON representation of an {@link AdFilters} object as would be generated by
+     *     {@link #toJson()}.
+     * @return An {@link AdFilters} object generated from the given JSON.
+     * @hide
+     */
+    public static AdFilters fromJson(JSONObject json) throws JSONException {
+        Builder builder = new Builder();
+        if (json.has(FREQUENCY_CAP_FIELD_NAME)) {
+            builder.setFrequencyCapFilters(
+                    FrequencyCapFilters.fromJson(json.getJSONObject(FREQUENCY_CAP_FIELD_NAME)));
+        }
+        if (json.has(APP_INSTALL_FIELD_NAME)) {
+            builder.setAppInstallFilters(
+                    AppInstallFilters.fromJson(json.getJSONObject(APP_INSTALL_FIELD_NAME)));
+        }
+        return builder.build();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mFrequencyCapFilters,
+                (targetParcel, sourceFilters) -> sourceFilters.writeToParcel(targetParcel, flags));
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mAppInstallFilters,
+                (targetParcel, sourceFilters) -> sourceFilters.writeToParcel(targetParcel, flags));
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Checks whether the {@link AdFilters} objects represent the same set of filters. */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AdFilters)) return false;
+        AdFilters adFilters = (AdFilters) o;
+        return Objects.equals(mFrequencyCapFilters, adFilters.mFrequencyCapFilters)
+                && Objects.equals(mAppInstallFilters, adFilters.mAppInstallFilters);
+    }
+
+    /** Returns the hash of the {@link AdFilters} object's data. */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mFrequencyCapFilters, mAppInstallFilters);
+    }
+
+    @Override
+    public String toString() {
+        return "AdFilters{"
+                + "mFrequencyCapFilters="
+                + mFrequencyCapFilters
+                + ", mAppInstallFilters="
+                + mAppInstallFilters
+                + '}';
+    }
+
+    /** Builder for creating {@link AdFilters} objects. */
+    public static final class Builder {
+        @Nullable private FrequencyCapFilters mFrequencyCapFilters;
+        @Nullable private AppInstallFilters mAppInstallFilters;
+
+        public Builder() {}
+
+        /**
+         * Sets the {@link FrequencyCapFilters} which will apply to the ad.
+         *
+         * <p>If set to {@code null} or not set, no frequency cap filters will be associated with
+         * the ad.
+         */
+        @NonNull
+        public Builder setFrequencyCapFilters(@Nullable FrequencyCapFilters frequencyCapFilters) {
+            mFrequencyCapFilters = frequencyCapFilters;
+            return this;
+        }
+
+        /**
+         * Sets the {@link AppInstallFilters} which will apply to the ad.
+         *
+         * <p>If set to {@code null} or not set, no app install filters will be associated with the
+         * ad.
+         */
+        @FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
+        @NonNull
+        public Builder setAppInstallFilters(@Nullable AppInstallFilters appInstallFilters) {
+            mAppInstallFilters = appInstallFilters;
+            return this;
+        }
+
+        /** Builds and returns an {@link AdFilters} instance. */
+        @NonNull
+        public AdFilters build() {
+            return new AdFilters(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/common/AdSelectionSignals.java b/android-35/android/adservices/common/AdSelectionSignals.java
new file mode 100644
index 0000000..41fdb1d
--- /dev/null
+++ b/android-35/android/adservices/common/AdSelectionSignals.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.common;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
+/**
+ * This class holds JSON that will be passed into a JavaScript function during ad selection. Its
+ * contents are not used by <a
+ * href="https://developer.android.com/design-for-safety/privacy-sandbox/fledge">FLEDGE</a> platform
+ * code, but are merely validated and then passed to the appropriate JavaScript ad selection
+ * function.
+ */
+public final class AdSelectionSignals implements Parcelable {
+
+    public static final AdSelectionSignals EMPTY = fromString("{}");
+
+    @NonNull private final String mSignals;
+
+    private AdSelectionSignals(@NonNull Parcel in) {
+        this(in.readString());
+    }
+
+    private AdSelectionSignals(@NonNull String adSelectionSignals) {
+        this(adSelectionSignals, true);
+    }
+
+    private AdSelectionSignals(@NonNull String adSelectionSignals, boolean validate) {
+        Objects.requireNonNull(adSelectionSignals);
+        if (validate) {
+            validate(adSelectionSignals);
+        }
+        mSignals = adSelectionSignals;
+    }
+
+    @NonNull
+    public static final Creator<AdSelectionSignals> CREATOR =
+            new Creator<AdSelectionSignals>() {
+                @Override
+                public AdSelectionSignals createFromParcel(Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new AdSelectionSignals(in);
+                }
+
+                @Override
+                public AdSelectionSignals[] newArray(int size) {
+                    return new AdSelectionSignals[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mSignals);
+    }
+
+    /**
+     * Compares this AdSelectionSignals to the specified object. The result is true if and only if
+     * the argument is not null and is a AdSelectionSignals object with the same string form
+     * (obtained by calling {@link #toString()}). Note that this method will not perform any JSON
+     * normalization so two AdSelectionSignals objects with the same JSON could be not equal if the
+     * String representations of the objects was not equal.
+     *
+     * @param o The object to compare this AdSelectionSignals against
+     * @return true if the given object represents an AdSelectionSignals equivalent to this
+     *     AdSelectionSignals, false otherwise
+     */
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof AdSelectionSignals
+                && mSignals.equals(((AdSelectionSignals) o).toString());
+    }
+
+    /**
+     * Returns a hash code corresponding to the string representation of this class obtained by
+     * calling {@link #toString()}. Note that this method will not perform any JSON normalization so
+     * two AdSelectionSignals objects with the same JSON could have different hash codes if the
+     * underlying string representation was different.
+     *
+     * @return a hash code value for this object.
+     */
+    @Override
+    public int hashCode() {
+        return mSignals.hashCode();
+    }
+
+    /** @return The String form of the JSON wrapped by this class. */
+    @Override
+    @NonNull
+    public String toString() {
+        return mSignals;
+    }
+
+    /**
+     * Creates an AdSelectionSignals from a given JSON in String form.
+     *
+     * @param source Any valid JSON string to create the AdSelectionSignals with.
+     * @return An AdSelectionSignals object wrapping the given String.
+     */
+    @NonNull
+    public static AdSelectionSignals fromString(@NonNull String source) {
+        return new AdSelectionSignals(source, true);
+    }
+
+    /**
+     * Creates an AdSelectionSignals from a given JSON in String form.
+     *
+     * @param source Any valid JSON string to create the AdSelectionSignals with.
+     * @param validate Construction-time validation is run on the string if and only if this is
+     *     true.
+     * @return An AdSelectionSignals object wrapping the given String.
+     * @hide
+     */
+    @NonNull
+    public static AdSelectionSignals fromString(@NonNull String source, boolean validate) {
+        return new AdSelectionSignals(source, validate);
+    }
+
+    /**
+     * @return the signal's String form data size in bytes.
+     * @hide
+     */
+    public int getSizeInBytes() {
+        return this.mSignals.getBytes(StandardCharsets.UTF_8).length;
+    }
+
+    private void validate(String inputString) {
+        // TODO(b/238849930) Bring the existing validation function in here
+    }
+}
diff --git a/android-35/android/adservices/common/AdServicesCommonManager.java b/android-35/android/adservices/common/AdServicesCommonManager.java
new file mode 100644
index 0000000..10a7975
--- /dev/null
+++ b/android-35/android/adservices/common/AdServicesCommonManager.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.common;
+
+import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_STATE;
+import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_STATE_COMPAT;
+import static android.adservices.common.AdServicesPermissions.MODIFY_ADSERVICES_STATE;
+import static android.adservices.common.AdServicesPermissions.MODIFY_ADSERVICES_STATE_COMPAT;
+import static android.adservices.common.AdServicesPermissions.UPDATE_PRIVILEGED_AD_ID;
+import static android.adservices.common.AdServicesPermissions.UPDATE_PRIVILEGED_AD_ID_COMPAT;
+
+import android.adservices.adid.AdId;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.app.sdksandbox.SandboxedSdkContext;
+import android.content.Context;
+import android.os.Build;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+import android.os.SystemClock;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.adservices.AdServicesCommon;
+import com.android.adservices.LogUtil;
+import com.android.adservices.ServiceBinder;
+import com.android.adservices.flags.Flags;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * AdServicesCommonManager contains APIs common across the various AdServices. It provides two
+ * SystemApis:
+ *
+ * <ul>
+ *   <li>isAdServicesEnabled - allows to get AdServices state.
+ *   <li>setAdServicesEntryPointEnabled - allows to control AdServices state.
+ * </ul>
+ *
+ * <p>The instance of the {@link AdServicesCommonManager} can be obtained using {@link
+ * Context#getSystemService} and {@link AdServicesCommonManager} class.
+ *
+ * @hide
+ */
+@SystemApi
+public class AdServicesCommonManager {
+    /** @hide */
+    public static final String AD_SERVICES_COMMON_SERVICE = "ad_services_common_service";
+
+    private final Context mContext;
+    private final ServiceBinder<IAdServicesCommonService> mAdServicesCommonServiceBinder;
+
+    /**
+     * Create AdServicesCommonManager.
+     *
+     * @hide
+     */
+    public AdServicesCommonManager(@NonNull Context context) {
+        mContext = context;
+        mAdServicesCommonServiceBinder =
+                ServiceBinder.getServiceBinder(
+                        context,
+                        AdServicesCommon.ACTION_AD_SERVICES_COMMON_SERVICE,
+                        IAdServicesCommonService.Stub::asInterface);
+    }
+
+    /**
+     * Factory method for creating an instance of AdServicesCommonManager.
+     *
+     * @param context The {@link Context} to use
+     * @return A {@link AdServicesCommonManager} instance
+     */
+    @NonNull
+    public static AdServicesCommonManager get(@NonNull Context context) {
+        // On T+, context.getSystemService() does more than just call constructor.
+        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+                ? context.getSystemService(AdServicesCommonManager.class)
+                : new AdServicesCommonManager(context);
+    }
+
+    @NonNull
+    private IAdServicesCommonService getService() {
+        IAdServicesCommonService service = mAdServicesCommonServiceBinder.getService();
+        if (service == null) {
+            throw new IllegalStateException("Unable to find the service");
+        }
+        return service;
+    }
+
+    /**
+     * Get the AdService's enablement state which represents whether AdServices feature is enabled
+     * or not. This API is for Android S+, which has the OutcomeReceiver class available.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {ACCESS_ADSERVICES_STATE, ACCESS_ADSERVICES_STATE_COMPAT})
+    @RequiresApi(Build.VERSION_CODES.S)
+    public void isAdServicesEnabled(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, Exception> callback) {
+        isAdServicesEnabled(
+                executor, OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
+    }
+
+    /**
+     * Get the AdService's enablement state which represents whether AdServices feature is enabled
+     * or not. This API is for Android R, and uses the AdServicesOutcomeReceiver class because
+     * OutcomeReceiver is not available.
+     *
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ADSERVICES_ENABLEMENT_CHECK_ENABLED)
+    @RequiresPermission(anyOf = {ACCESS_ADSERVICES_STATE, ACCESS_ADSERVICES_STATE_COMPAT})
+    public void isAdServicesEnabled(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AdServicesOutcomeReceiver<Boolean, Exception> callback) {
+        final IAdServicesCommonService service = getService();
+        try {
+            service.isAdServicesEnabled(
+                    new IAdServicesCommonCallback.Stub() {
+                        @Override
+                        public void onResult(IsAdServicesEnabledResult result) {
+                            executor.execute(
+                                    () -> {
+                                        callback.onResult(result.getAdServicesEnabled());
+                                    });
+                        }
+
+                        @Override
+                        public void onFailure(int statusCode) {
+                            executor.execute(
+                                    () ->
+                                            callback.onError(
+                                                    AdServicesStatusUtils.asException(statusCode)));
+                        }
+                    });
+        } catch (RemoteException e) {
+            LogUtil.e(e, "RemoteException");
+            executor.execute(
+                    () -> callback.onError(new IllegalStateException("Internal Error!", e)));
+        }
+    }
+
+    /**
+     * Sets the AdService's enablement state based on the provided parameters.
+     *
+     * <p>As a result of the AdServices state, {@code adServicesEntryPointEnabled}, {@code
+     * adIdEnabled}, appropriate notification may be displayed to the user. It's displayed only once
+     * when all the following conditions are met:
+     *
+     * <ul>
+     *   <li>AdServices state - enabled.
+     *   <li>adServicesEntryPointEnabled - true.
+     * </ul>
+     *
+     * @param adServicesEntryPointEnabled indicate entry point enabled or not
+     * @param adIdEnabled indicate user opt-out of adid or not
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {MODIFY_ADSERVICES_STATE, MODIFY_ADSERVICES_STATE_COMPAT})
+    public void setAdServicesEnabled(boolean adServicesEntryPointEnabled, boolean adIdEnabled) {
+        final IAdServicesCommonService service = getService();
+        try {
+            service.setAdServicesEnabled(adServicesEntryPointEnabled, adIdEnabled);
+        } catch (RemoteException e) {
+            LogUtil.e(e, "RemoteException");
+        }
+    }
+
+    /**
+     * Enable AdServices based on the AdServicesStates input parameter. This API is for Android S+,
+     * which has the OutcomeReceiver class available.
+     *
+     * <p>Based on the provided {@code AdServicesStates}, AdServices may be enabled. Specifically,
+     * users will be provided with an enrollment channel (such as notification) to become privacy
+     * sandbox users when:
+     *
+     * <ul>
+     *   <li>isAdServicesUiEnabled - true.
+     *   <li>isU18Account | isAdultAccount - true.
+     * </ul>
+     *
+     * @param {@code AdServicesStates} parcel containing relevant AdServices state variables.
+     * @return false if API is disabled, true if the API call completed successfully. Otherwise, it
+     *     would return one of the following exceptions to the user:
+     *     <ul>
+     *       <li>IllegalStateException - the default exception thrown when service crashes
+     *           unexpectedly.
+     *       <li>SecurityException - when the caller is not authorized to call this API.
+     *       <li>TimeoutException - when the services takes too long to respond.
+     *     </ul>
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {MODIFY_ADSERVICES_STATE, MODIFY_ADSERVICES_STATE_COMPAT})
+    @RequiresApi(Build.VERSION_CODES.S)
+    public void enableAdServices(
+            @NonNull AdServicesStates adServicesStates,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, Exception> callback) {
+        enableAdServices(
+                adServicesStates,
+                executor,
+                OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
+    }
+
+    /**
+     * Enable AdServices based on the AdServicesStates input parameter. This API is for Android R,
+     * and uses the AdServicesOutcomeReceiver class because OutcomeReceiver is not available.
+     *
+     * <p>Based on the provided {@code AdServicesStates}, AdServices may be enabled. Specifically,
+     * users will be provided with an enrollment channel (such as notification) to become privacy
+     * sandbox users when:
+     *
+     * <ul>
+     *   <li>isAdServicesUiEnabled - true.
+     *   <li>isU18Account | isAdultAccount - true.
+     * </ul>
+     *
+     * @param adServicesStates parcel containing relevant AdServices state variables.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ENABLE_ADSERVICES_API_ENABLED)
+    @RequiresPermission(anyOf = {MODIFY_ADSERVICES_STATE, MODIFY_ADSERVICES_STATE_COMPAT})
+    public void enableAdServices(
+            @NonNull AdServicesStates adServicesStates,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AdServicesOutcomeReceiver<Boolean, Exception> callback) {
+        Objects.requireNonNull(adServicesStates);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        final IAdServicesCommonService service = getService();
+        try {
+            service.enableAdServices(
+                    adServicesStates,
+                    new IEnableAdServicesCallback.Stub() {
+                        @Override
+                        public void onResult(EnableAdServicesResponse response) {
+                            executor.execute(
+                                    () -> {
+                                        if (!response.isApiEnabled()) {
+                                            callback.onResult(false);
+                                            return;
+                                        }
+
+                                        if (response.isSuccess()) {
+                                            callback.onResult(true);
+                                        } else {
+                                            callback.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            response.getStatusCode()));
+                                        }
+                                    });
+                        }
+
+                        @Override
+                        public void onFailure(int statusCode) {
+                            executor.execute(
+                                    () ->
+                                            callback.onError(
+                                                    AdServicesStatusUtils.asException(statusCode)));
+                        }
+                    });
+        } catch (RemoteException e) {
+            LogUtil.e(e, "RemoteException");
+            executor.execute(
+                    () -> callback.onError(new IllegalStateException("Internal Error!", e)));
+        }
+    }
+
+    /**
+     * Updates {@link AdId} in Adservices when the device changes {@link AdId}. This API is used by
+     * AdIdProvider.
+     *
+     * @param updateAdIdRequest the request that contains {@link AdId} information to update.
+     * @param executor the executor for the callback.
+     * @param callback the callback in type {@link AdServicesOutcomeReceiver}, available on Android
+     *     R and above.
+     * @throws IllegalStateException when service is not available or the feature is not enabled, or
+     *     if there is any {@code Binder} invocation error.
+     * @throws SecurityException when the caller is not authorized to call this API.
+     * @hide
+     */
+    // TODO(b/295205476): Move exceptions into the callback.
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_AD_ID_CACHE_ENABLED)
+    @RequiresPermission(anyOf = {UPDATE_PRIVILEGED_AD_ID, UPDATE_PRIVILEGED_AD_ID_COMPAT})
+    public void updateAdId(
+            @NonNull UpdateAdIdRequest updateAdIdRequest,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AdServicesOutcomeReceiver<Boolean, Exception> callback) {
+        Objects.requireNonNull(updateAdIdRequest);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        IAdServicesCommonService service = getService();
+        try {
+            service.updateAdIdCache(
+                    updateAdIdRequest,
+                    new IUpdateAdIdCallback.Stub() {
+                        @Override
+                        public void onResult(String message) {
+                            executor.execute(() -> callback.onResult(true));
+                        }
+
+                        @Override
+                        public void onFailure(int statusCode) {
+                            executor.execute(
+                                    () ->
+                                            callback.onError(
+                                                    AdServicesStatusUtils.asException(statusCode)));
+                        }
+                    });
+        } catch (RemoteException e) {
+            LogUtil.e(e, "RemoteException calling updateAdIdCache with %s", updateAdIdRequest);
+            executor.execute(
+                    () -> callback.onError(new IllegalStateException("Internal Error!", e)));
+        }
+    }
+
+    /**
+     * Updates {@link AdId} in Adservices when the device changes {@link AdId}. This API is used by
+     * AdIdProvider.
+     *
+     * @param updateAdIdRequest the request that contains {@link AdId} information to update.
+     * @param executor the executor for the callback.
+     * @param callback the callback in type {@link OutcomeReceiver}, available on Android S and
+     *     above.
+     * @throws IllegalStateException when service is not available or the feature is not enabled, or
+     *     if there is any {@code Binder} invocation error.
+     * @throws SecurityException when the caller is not authorized to call this API.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_AD_ID_CACHE_ENABLED)
+    @RequiresPermission(anyOf = {UPDATE_PRIVILEGED_AD_ID, UPDATE_PRIVILEGED_AD_ID_COMPAT})
+    @RequiresApi(Build.VERSION_CODES.S)
+    public void updateAdId(
+            @NonNull UpdateAdIdRequest updateAdIdRequest,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, Exception> callback) {
+        updateAdId(
+                updateAdIdRequest,
+                executor,
+                OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
+    }
+
+    /**
+     * Get the AdService's common states.
+     *
+     * @param executor the executor for the callback.
+     * @param callback the callback in type {@link AdServicesOutcomeReceiver}, available on Android
+     *     R and above.
+     * @throws IllegalStateException if there is any {@code Binder} invocation error.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_GET_ADSERVICES_COMMON_STATES_API_ENABLED)
+    @RequiresPermission(anyOf = {ACCESS_ADSERVICES_STATE, ACCESS_ADSERVICES_STATE_COMPAT})
+    public void getAdservicesCommonStates(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull
+                    AdServicesOutcomeReceiver<AdServicesCommonStatesResponse, Exception> callback) {
+        final IAdServicesCommonService service = getService();
+        CallerMetadata callerMetadata =
+                new CallerMetadata.Builder()
+                        .setBinderElapsedTimestamp(SystemClock.elapsedRealtime())
+                        .build();
+        String appPackageName = "";
+        String sdkPackageName = "";
+        // First check if context is SandboxedSdkContext or not
+        SandboxedSdkContext sandboxedSdkContext =
+                SandboxedSdkContextUtils.getAsSandboxedSdkContext(mContext);
+        if (sandboxedSdkContext != null) {
+            // This is the case with the Sandbox.
+            sdkPackageName = sandboxedSdkContext.getSdkPackageName();
+            appPackageName = sandboxedSdkContext.getClientPackageName();
+        } else {
+            // This is the case without the Sandbox.
+            appPackageName = mContext.getPackageName();
+        }
+        try {
+            service.getAdServicesCommonStates(
+                    new GetAdServicesCommonStatesParams.Builder(appPackageName, sdkPackageName)
+                            .build(),
+                    callerMetadata,
+                    new IAdServicesCommonStatesCallback.Stub() {
+                        @Override
+                        public void onResult(AdServicesCommonStatesResponse result) {
+                            executor.execute(
+                                    () -> {
+                                        callback.onResult(result);
+                                    });
+                        }
+
+                        @Override
+                        public void onFailure(int statusCode) {
+                            executor.execute(
+                                    () ->
+                                            callback.onError(
+                                                    AdServicesStatusUtils.asException(statusCode)));
+                        }
+                    });
+        } catch (RemoteException e) {
+            LogUtil.e(e, "RemoteException");
+            executor.execute(
+                    () -> callback.onError(new IllegalStateException("Internal Error!", e)));
+        }
+    }
+}
diff --git a/android-35/android/adservices/common/AdServicesCommonStates.java b/android-35/android/adservices/common/AdServicesCommonStates.java
new file mode 100644
index 0000000..32515da
--- /dev/null
+++ b/android-35/android/adservices/common/AdServicesCommonStates.java
@@ -0,0 +1,153 @@
+/*
+ * 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 android.adservices.common;
+
+import static android.adservices.common.ConsentStatus.ConsentStatusCode;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.flags.Flags;
+
+import java.util.Objects;
+
+/**
+ * Represent the common states from the getAdservicesCommonStates API.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_GET_ADSERVICES_COMMON_STATES_API_ENABLED)
+public final class AdServicesCommonStates implements Parcelable {
+    @ConsentStatusCode private final int mMeasurementState;
+    @ConsentStatusCode private final int mPaState;
+
+    /**
+     * Creates an object which represents the result from the getAdservicesCommonStates API.
+     *
+     * @param measurementState a {@link ConsentStatusCode} int indicating whether meansurement is
+     *     allowed
+     * @param paState a {@link ConsentStatusCode} indicating whether fledge is allowed
+     */
+    private AdServicesCommonStates(
+            @ConsentStatusCode int measurementState, @ConsentStatusCode int paState) {
+        this.mMeasurementState = measurementState;
+        this.mPaState = paState;
+    }
+
+    private AdServicesCommonStates(@NonNull Parcel in) {
+        this.mMeasurementState = in.readInt();
+        this.mPaState = in.readInt();
+    }
+
+    @NonNull
+    public static final Creator<AdServicesCommonStates> CREATOR =
+            new Creator<AdServicesCommonStates>() {
+                @Override
+                public AdServicesCommonStates createFromParcel(Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new AdServicesCommonStates(in);
+                }
+
+                @Override
+                public AdServicesCommonStates[] newArray(int size) {
+                    return new AdServicesCommonStates[size];
+                }
+            };
+
+    /** describe contents for parcel */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** write contents for parcel */
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mMeasurementState);
+        out.writeInt(mPaState);
+    }
+
+    /** Get the measurement allowed state. */
+    @ConsentStatusCode
+    public int getMeasurementState() {
+        return mMeasurementState;
+    }
+
+    /** Get the fledge allowed state. */
+    @ConsentStatusCode
+    public int getPaState() {
+        return mPaState;
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) return true;
+        if (!(object instanceof AdServicesCommonStates)) return false;
+        AdServicesCommonStates adservicesCommonStates = (AdServicesCommonStates) object;
+        return getMeasurementState() == adservicesCommonStates.getMeasurementState()
+                && getPaState() == adservicesCommonStates.getPaState();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getMeasurementState(), getPaState());
+    }
+
+    @Override
+    public String toString() {
+        return "AdservicesCommonStates{"
+                + "mMeasurementState="
+                + mMeasurementState
+                + ", mPaState="
+                + mPaState
+                + '}';
+    }
+
+    /**
+     * Builder for {@link AdServicesCommonStates} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        @ConsentStatusCode private int mMeasurementState;
+        @ConsentStatusCode private int mPaState;
+
+        public Builder() {}
+
+        /** Set the measurement allowed by the getAdServicesCommonStates API */
+        @NonNull
+        public AdServicesCommonStates.Builder setMeasurementState(
+                @ConsentStatusCode int measurementState) {
+            mMeasurementState = measurementState;
+            return this;
+        }
+
+        /** Set the pa allowed by the getAdServicesCommonStates API. */
+        @NonNull
+        public AdServicesCommonStates.Builder setPaState(@ConsentStatusCode int paState) {
+            mPaState = paState;
+            return this;
+        }
+
+        /** Builds a {@link AdServicesCommonStates} instance. */
+        @NonNull
+        public AdServicesCommonStates build() {
+            return new AdServicesCommonStates(mMeasurementState, mPaState);
+        }
+    }
+}
diff --git a/android-35/android/adservices/common/AdServicesCommonStatesResponse.java b/android-35/android/adservices/common/AdServicesCommonStatesResponse.java
new file mode 100644
index 0000000..ff5ace5
--- /dev/null
+++ b/android-35/android/adservices/common/AdServicesCommonStatesResponse.java
@@ -0,0 +1,120 @@
+/*
+ * 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 android.adservices.common;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.flags.Flags;
+
+import java.util.Objects;
+
+/**
+ * Response parcel of the getAdservicesCommonStates API.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_GET_ADSERVICES_COMMON_STATES_API_ENABLED)
+public final class AdServicesCommonStatesResponse implements Parcelable {
+
+    private AdServicesCommonStates mAdServicesCommonStates;
+
+    private AdServicesCommonStatesResponse(AdServicesCommonStates adservicesCommonStates) {
+        mAdServicesCommonStates = adservicesCommonStates;
+    }
+
+    private AdServicesCommonStatesResponse(@NonNull Parcel in) {
+        mAdServicesCommonStates = in.readParcelable(AdServicesCommonStates.class.getClassLoader());
+    }
+
+    @NonNull
+    public AdServicesCommonStates getAdServicesCommonStates() {
+        return mAdServicesCommonStates;
+    }
+
+    @NonNull
+    public static final Creator<AdServicesCommonStatesResponse> CREATOR =
+            new Creator<AdServicesCommonStatesResponse>() {
+                @Override
+                public AdServicesCommonStatesResponse createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new AdServicesCommonStatesResponse(in);
+                }
+
+                @Override
+                public AdServicesCommonStatesResponse[] newArray(int size) {
+                    return new AdServicesCommonStatesResponse[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        dest.writeParcelable(mAdServicesCommonStates, flags);
+    }
+
+    @Override
+    public String toString() {
+        return "EnableAdServicesResponse{"
+                + "mAdservicesCommonStates="
+                + mAdServicesCommonStates
+                + "'}";
+    }
+
+    /**
+     * Builder for {@link AdServicesCommonStatesResponse} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private AdServicesCommonStates mAdServicesCommonStates;
+
+        public Builder(@NonNull AdServicesCommonStates adservicesCommonStates) {
+            mAdServicesCommonStates = adservicesCommonStates;
+        }
+
+        /** Set the enableAdServices API response status Code. */
+        @NonNull
+        public AdServicesCommonStatesResponse.Builder setAdservicesCommonStates(
+                AdServicesCommonStates adservicesCommonStates) {
+            mAdServicesCommonStates = adservicesCommonStates;
+            return this;
+        }
+
+        /**
+         * Builds a {@link AdServicesCommonStatesResponse} instance.
+         *
+         * <p>throws IllegalArgumentException if any of the status code is null or error message is
+         * not set for an unsuccessful status.
+         */
+        @NonNull
+        public AdServicesCommonStatesResponse build() {
+            return new AdServicesCommonStatesResponse(mAdServicesCommonStates);
+        }
+    }
+}
diff --git a/android-35/android/adservices/common/AdServicesOutcomeReceiver.java b/android-35/android/adservices/common/AdServicesOutcomeReceiver.java
new file mode 100644
index 0000000..10aa1a4
--- /dev/null
+++ b/android-35/android/adservices/common/AdServicesOutcomeReceiver.java
@@ -0,0 +1,47 @@
+/*
+ * 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 android.adservices.common;
+
+import android.annotation.NonNull;
+
+/**
+ * Callback interface intended for use when an asynchronous operation may result in a failure. Exact
+ * copy of the {@link android.os.OutcomeReceiver} class, re-defined in the AdServices package for
+ * backwards compatibility to Android R.
+ *
+ * <p>This interface may be used in cases where an asynchronous API may complete either with a value
+ * or with a {@link Throwable} that indicates an error.
+ *
+ * @param <R> The type of the result that's being sent.
+ * @param <E> The type of the {@link Throwable} that contains more information about the error.
+ */
+public interface AdServicesOutcomeReceiver<R, E extends Throwable> {
+    /**
+     * Called when the asynchronous operation succeeds and delivers a result value.
+     *
+     * @param result The value delivered by the asynchronous operation.
+     */
+    void onResult(R result);
+
+    /**
+     * Called when the asynchronous operation fails. The mode of failure is indicated by the {@link
+     * Throwable} passed as an argument to this method.
+     *
+     * @param error A subclass of {@link Throwable} with more details about the error that occurred.
+     */
+    default void onError(@NonNull E error) {}
+}
diff --git a/android-35/android/adservices/common/AdServicesPermissions.java b/android-35/android/adservices/common/AdServicesPermissions.java
new file mode 100644
index 0000000..ca21c9b
--- /dev/null
+++ b/android-35/android/adservices/common/AdServicesPermissions.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.common;
+
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+
+import com.android.adservices.flags.Flags;
+
+/** Permissions used by the AdServices APIs. */
+public class AdServicesPermissions {
+    private AdServicesPermissions() {}
+
+    /** This permission needs to be declared by the caller of Topics APIs. */
+    public static final String ACCESS_ADSERVICES_TOPICS =
+            "android.permission.ACCESS_ADSERVICES_TOPICS";
+
+    /** This permission needs to be declared by the caller of Attribution APIs. */
+    public static final String ACCESS_ADSERVICES_ATTRIBUTION =
+            "android.permission.ACCESS_ADSERVICES_ATTRIBUTION";
+
+    /** This permission needs to be declared by the caller of Custom Audiences APIs. */
+    public static final String ACCESS_ADSERVICES_CUSTOM_AUDIENCE =
+            "android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCE";
+
+    /** This permission needs to be declared by the caller of Protected Signals APIs. */
+    @FlaggedApi(Flags.FLAG_PROTECTED_SIGNALS_ENABLED)
+    public static final String ACCESS_ADSERVICES_PROTECTED_SIGNALS =
+            "android.permission.ACCESS_ADSERVICES_PROTECTED_SIGNALS";
+
+    /** This permission needs to be declared by the caller of Protected Signals APIs. */
+    @SuppressWarnings("FlaggedApi") // aconfig not available on this branch
+    @FlaggedApi(Flags.FLAG_PROTECTED_SIGNALS_ENABLED)
+    public static final String ACCESS_ADSERVICES_AD_SELECTION =
+            "android.permission.ACCESS_ADSERVICES_AD_SELECTION";
+
+    /** This permission needs to be declared by the caller of Advertising ID APIs. */
+    public static final String ACCESS_ADSERVICES_AD_ID =
+            "android.permission.ACCESS_ADSERVICES_AD_ID";
+
+    /**
+     * This is a signature permission that needs to be declared by the AdServices apk to access API
+     * for AdID provided by another provider service. The signature permission is required to make
+     * sure that only AdServices is permitted to access this api.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACCESS_PRIVILEGED_AD_ID =
+            "android.permission.ACCESS_PRIVILEGED_AD_ID";
+
+    /**
+     * This is a signature permission needs to be declared by the AdServices apk to access API for
+     * AppSetId provided by another provider service. The signature permission is required to make
+     * sure that only AdServices is permitted to access this api.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACCESS_PRIVILEGED_APP_SET_ID =
+            "android.permission.ACCESS_PRIVILEGED_APP_SET_ID";
+
+    /**
+     * The permission that lets it modify AdService's enablement state modification API.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String MODIFY_ADSERVICES_STATE =
+            "android.permission.MODIFY_ADSERVICES_STATE";
+
+    /**
+     * The permission that lets it modify AdService's enablement state modification API on S-.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String MODIFY_ADSERVICES_STATE_COMPAT =
+            "android.permission.MODIFY_ADSERVICES_STATE_COMPAT";
+
+    /**
+     * The permission that lets it access AdService's enablement state modification API.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACCESS_ADSERVICES_STATE =
+            "android.permission.ACCESS_ADSERVICES_STATE";
+
+    /**
+     * The permission that lets it access AdService's enablement state modification API on S-.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACCESS_ADSERVICES_STATE_COMPAT =
+            "android.permission.ACCESS_ADSERVICES_STATE_COMPAT";
+
+    /**
+     * The permission needed to call AdServicesManager APIs
+     *
+     * @hide
+     */
+    public static final String ACCESS_ADSERVICES_MANAGER =
+            "android.permission.ACCESS_ADSERVICES_MANAGER";
+
+    /**
+     * This is a signature permission needs to be declared by the AdServices apk to access API for
+     * AdServices Cobalt upload service provided by another provider service. The signature
+     * permission is required to make sure that only AdServices is permitted to access this api.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACCESS_PRIVILEGED_ADSERVICES_COBALT_UPLOAD =
+            "android.permission.ACCESS_PRIVILEGED_AD_SERVICES_COBALT_UPLOAD";
+
+    /**
+     * The permission that allows calling updating AdId Cache API via Common Service.
+     *
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_AD_ID_CACHE_ENABLED)
+    public static final String UPDATE_PRIVILEGED_AD_ID =
+            "android.permission.UPDATE_PRIVILEGED_AD_ID";
+
+    /**
+     * The permission that allows calling updating AdId Cache API via Common Service on S-.
+     *
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_AD_ID_CACHE_ENABLED)
+    public static final String UPDATE_PRIVILEGED_AD_ID_COMPAT =
+            "android.permission.UPDATE_PRIVILEGED_AD_ID_COMPAT";
+}
diff --git a/android-35/android/adservices/common/AdServicesResponse.java b/android-35/android/adservices/common/AdServicesResponse.java
new file mode 100644
index 0000000..017fbbe
--- /dev/null
+++ b/android-35/android/adservices/common/AdServicesResponse.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.common;
+
+import static android.adservices.common.AdServicesStatusUtils.STATUS_SUCCESS;
+import static android.adservices.common.AdServicesStatusUtils.StatusCode;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Represents an abstract, generic response for AdServices APIs.
+ *
+ * @hide
+ */
+public class AdServicesResponse implements Parcelable {
+    @NonNull
+    public static final Creator<AdServicesResponse> CREATOR =
+            new Parcelable.Creator<AdServicesResponse>() {
+                @Override
+                public AdServicesResponse createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new AdServicesResponse(in);
+                }
+
+                @Override
+                public AdServicesResponse[] newArray(int size) {
+                    return new AdServicesResponse[size];
+                }
+            };
+
+    @StatusCode protected final int mStatusCode;
+    @Nullable protected final String mErrorMessage;
+
+    protected AdServicesResponse(@NonNull Builder builder) {
+        mStatusCode = builder.mStatusCode;
+        mErrorMessage = builder.mErrorMessage;
+    }
+
+    protected AdServicesResponse(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        mStatusCode = in.readInt();
+        mErrorMessage = in.readString();
+    }
+
+    protected AdServicesResponse(@StatusCode int statusCode, @Nullable String errorMessage) {
+        mStatusCode = statusCode;
+        mErrorMessage = errorMessage;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        dest.writeInt(mStatusCode);
+        dest.writeString(mErrorMessage);
+    }
+
+    /** Returns one of the {@code STATUS} constants defined in {@link StatusCode}. */
+    @StatusCode
+    public int getStatusCode() {
+        return mStatusCode;
+    }
+
+    /**
+     * Returns {@code true} if {@link #getStatusCode} is {@link
+     * AdServicesStatusUtils#STATUS_SUCCESS}.
+     */
+    public boolean isSuccess() {
+        return getStatusCode() == STATUS_SUCCESS;
+    }
+
+    /** Returns the error message associated with this response. */
+    @Nullable
+    public String getErrorMessage() {
+        return mErrorMessage;
+    }
+
+    /**
+     * Builder for {@link AdServicesResponse} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        @StatusCode private int mStatusCode = STATUS_SUCCESS;
+        @Nullable private String mErrorMessage;
+
+        public Builder() {}
+
+        /** Set the Status Code. */
+        @NonNull
+        public AdServicesResponse.Builder setStatusCode(@StatusCode int statusCode) {
+            mStatusCode = statusCode;
+            return this;
+        }
+
+        /** Set the Error Message. */
+        @NonNull
+        public AdServicesResponse.Builder setErrorMessage(@Nullable String errorMessage) {
+            mErrorMessage = errorMessage;
+            return this;
+        }
+
+        /** Builds a {@link AdServicesResponse} instance. */
+        @NonNull
+        public AdServicesResponse build() {
+            return new AdServicesResponse(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/common/AdServicesStates.java b/android-35/android/adservices/common/AdServicesStates.java
new file mode 100644
index 0000000..3a26ccb
--- /dev/null
+++ b/android-35/android/adservices/common/AdServicesStates.java
@@ -0,0 +1,181 @@
+/*
+ * 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 android.adservices.common;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * AdServicesStates exposed to system apps/services through the enableAdServices API. The bits
+ * stored in this parcel can change frequently based on user interaction with the Ads settings page.
+ *
+ * @hide
+ */
+@SystemApi
+public final class AdServicesStates implements Parcelable {
+
+    public static final @NonNull Creator<AdServicesStates> CREATOR =
+            new Parcelable.Creator<AdServicesStates>() {
+                @Override
+                public AdServicesStates createFromParcel(Parcel in) {
+                    return new AdServicesStates(in);
+                }
+
+                @Override
+                public AdServicesStates[] newArray(int size) {
+                    return new AdServicesStates[size];
+                }
+            };
+
+    private boolean mIsPrivacySandboxUiEnabled;
+    private boolean mIsPrivacySandboxUiRequest;
+    private boolean mIsU18Account;
+    private boolean mIsAdultAccount;
+    private boolean mIsAdIdEnabled;
+
+    private AdServicesStates(
+            boolean isPrivacySandboxUiEnabled,
+            boolean isPrivacySandboxUiRequest,
+            boolean isU18Account,
+            boolean isAdultAccount,
+            boolean isAdIdEnabled) {
+        mIsPrivacySandboxUiEnabled = isPrivacySandboxUiEnabled;
+        mIsPrivacySandboxUiRequest = isPrivacySandboxUiRequest;
+        mIsU18Account = isU18Account;
+        mIsAdultAccount = isAdultAccount;
+        mIsAdIdEnabled = isAdIdEnabled;
+    }
+
+    private AdServicesStates(@NonNull Parcel in) {
+        mIsPrivacySandboxUiEnabled = in.readBoolean();
+        mIsPrivacySandboxUiRequest = in.readBoolean();
+        mIsU18Account = in.readBoolean();
+        mIsAdultAccount = in.readBoolean();
+        mIsAdIdEnabled = in.readBoolean();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeBoolean(mIsPrivacySandboxUiEnabled);
+        out.writeBoolean(mIsPrivacySandboxUiRequest);
+        out.writeBoolean(mIsU18Account);
+        out.writeBoolean(mIsAdultAccount);
+        out.writeBoolean(mIsAdIdEnabled);
+    }
+
+    /** Returns whether the privacy sandbox UI is visible from the settings app. */
+    @NonNull
+    public boolean isPrivacySandboxUiEnabled() {
+        return mIsPrivacySandboxUiEnabled;
+    }
+
+    /**
+     * Returns whether the API call was the byproduct of a privacy sandbox UI request from the
+     * settings app.
+     */
+    @NonNull
+    public boolean isPrivacySandboxUiRequest() {
+        return mIsPrivacySandboxUiRequest;
+    }
+
+    /** Returns whether Advertising ID is enabled. */
+    @NonNull
+    public boolean isAdIdEnabled() {
+        return mIsAdIdEnabled;
+    }
+
+    /**
+     * Determines whether the user account is eligible for the U18 (under 18) privacy sandbox, in
+     * which all ads relevancepersonalized Ads APIs are * permanently disabled and the ad
+     * measurement API can be enabled/disabled by the user. An account is considered a U18 account
+     * if privacy sandbox has received signals that the user is a minor.
+     */
+    @NonNull
+    public boolean isU18Account() {
+        return mIsU18Account;
+    }
+
+    /**
+     * Determines whether the user account is eligible for the adult or full-fledged privacy
+     * sandbox, in which all Ads APIs can be * enabled/disabled by the user. An account is
+     * considered an adult account if privacy sandbox has received signals that the user is an
+     * adult.
+     */
+    @NonNull
+    public boolean isAdultAccount() {
+        return mIsAdultAccount;
+    }
+
+    /** Builder for {@link AdServicesStates} objects. */
+    public static final class Builder {
+        private boolean mIsPrivacySandboxUiEnabled;
+        private boolean mIsPrivacySandboxUiRequest;
+        private boolean mIsU18Account;
+        private boolean mIsAdultAccount;
+        private boolean mIsAdIdEnabled;
+
+        public Builder() {
+        }
+
+        /** Set if the privacy sandbox UX entry point is enabled. */
+        public @NonNull Builder setPrivacySandboxUiEnabled(boolean isPrivacySandboxUiEnabled) {
+            mIsPrivacySandboxUiEnabled = isPrivacySandboxUiEnabled;
+            return this;
+        }
+
+        /** Set if the API call was the result of a privacy sandbox UX entry point request. */
+        public @NonNull Builder setPrivacySandboxUiRequest(boolean isPrivacySandboxUiRequest) {
+            mIsPrivacySandboxUiRequest = isPrivacySandboxUiRequest;
+            return this;
+        }
+
+        /** Set if the device is currently running under an U18 account. */
+        public @NonNull Builder setU18Account(boolean isU18Account) {
+            mIsU18Account = isU18Account;
+            return this;
+        }
+
+        /** Set if the device is currently running under an adult account. */
+        public @NonNull Builder setAdultAccount(boolean isAdultAccount) {
+            mIsAdultAccount = isAdultAccount;
+            return this;
+        }
+
+        /** Set if user has opt-in/out of Advertising ID. */
+        public @NonNull Builder setAdIdEnabled(boolean isAdIdEnabled) {
+            mIsAdIdEnabled = isAdIdEnabled;
+            return this;
+        }
+
+        /** Builds a {@link AdServicesStates} instance. */
+        public @NonNull AdServicesStates build() {
+            return new AdServicesStates(
+                    mIsPrivacySandboxUiEnabled,
+                    mIsPrivacySandboxUiRequest,
+                    mIsU18Account,
+                    mIsAdultAccount,
+                    mIsAdIdEnabled);
+        }
+    }
+}
diff --git a/android-35/android/adservices/common/AdServicesStatusUtils.java b/android-35/android/adservices/common/AdServicesStatusUtils.java
new file mode 100644
index 0000000..e5403a3
--- /dev/null
+++ b/android-35/android/adservices/common/AdServicesStatusUtils.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.common;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.LimitExceededException;
+
+import com.android.adservices.shared.common.ServiceUnavailableException;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Utility class containing status codes and functions used by various response objects.
+ *
+ * <p>Those status codes are internal only.
+ *
+ * @hide
+ */
+public final class AdServicesStatusUtils {
+
+    /**
+     * The status code has not been set. Keep unset status code the lowest value of the status
+     * codes.
+     */
+    public static final int STATUS_UNSET = -1;
+
+    /** The call was successful. */
+    public static final int STATUS_SUCCESS = 0;
+
+    /**
+     * An internal error occurred within the API, which the caller cannot address.
+     *
+     * <p>This error may be considered similar to {@link IllegalStateException}.
+     */
+    public static final int STATUS_INTERNAL_ERROR = 1;
+
+    /**
+     * The caller supplied invalid arguments to the call.
+     *
+     * <p>This error may be considered similar to {@link IllegalArgumentException}.
+     */
+    public static final int STATUS_INVALID_ARGUMENT = 2;
+
+    /** There was an unknown error. */
+    public static final int STATUS_UNKNOWN_ERROR = 3;
+
+    /**
+     * There was an I/O error.
+     *
+     * <p>This error may be considered similar to {@link IOException}.
+     */
+    public static final int STATUS_IO_ERROR = 4;
+
+    /**
+     * Result code for Rate Limit Reached.
+     *
+     * <p>This error may be considered similar to {@link LimitExceededException}.
+     */
+    public static final int STATUS_RATE_LIMIT_REACHED = 5;
+
+    /**
+     * Killswitch was enabled. AdServices is not available.
+     *
+     * <p>This error may be considered similar to {@link IllegalStateException}.
+     */
+    public static final int STATUS_KILLSWITCH_ENABLED = 6;
+
+    /**
+     * User consent was revoked. AdServices is not available.
+     *
+     * <p>This error may be considered similar to {@link IllegalStateException}.
+     */
+    public static final int STATUS_USER_CONSENT_REVOKED = 7;
+
+    /**
+     * AdServices were disabled. AdServices is not available.
+     *
+     * <p>This error may be considered similar to {@link IllegalStateException}.
+     */
+    public static final int STATUS_ADSERVICES_DISABLED = 8;
+
+    /**
+     * The caller is not authorized to make this call. Permission was not requested.
+     *
+     * <p>This error may be considered similar to {@link SecurityException}.
+     */
+    public static final int STATUS_PERMISSION_NOT_REQUESTED = 9;
+
+    /**
+     * The caller is not authorized to make this call. Caller is not allowed (not present in the
+     * allowed list).
+     *
+     * <p>This error may be considered similar to {@link SecurityException}.
+     */
+    public static final int STATUS_CALLER_NOT_ALLOWED = 10;
+
+    /**
+     * The caller is not authorized to make this call. Call was executed from background thread.
+     *
+     * <p>This error may be considered similar to {@link IllegalStateException}.
+     */
+    public static final int STATUS_BACKGROUND_CALLER = 11;
+
+    /**
+     * The caller is not authorized to make this call.
+     *
+     * <p>This error may be considered similar to {@link SecurityException}.
+     */
+    public static final int STATUS_UNAUTHORIZED = 12;
+
+    /**
+     * There was an internal Timeout within the API, which is non-recoverable by the caller
+     *
+     * <p>This error may be considered similar to {@link java.util.concurrent.TimeoutException}
+     */
+    public static final int STATUS_TIMEOUT = 13;
+
+    /**
+     * The device is not running a version of WebView that supports JSSandbox, required for FLEDGE
+     * Ad Selection.
+     *
+     * <p>This error may be considered similar to {@link IllegalStateException}.
+     */
+    public static final int STATUS_JS_SANDBOX_UNAVAILABLE = 14;
+
+    /**
+     * The service received an invalid object from the remote server.
+     *
+     * <p>This error may be considered similar to {@link InvalidObjectException}.
+     */
+    public static final int STATUS_INVALID_OBJECT = 15;
+
+    /**
+     * The caller is not authorized to make this call because it crosses user boundaries.
+     *
+     * <p>This error may be considered similar to {@link SecurityException}.
+     */
+    public static final int STATUS_CALLER_NOT_ALLOWED_TO_CROSS_USER_BOUNDARIES = 16;
+
+    /**
+     * Result code for Server Rate Limit Reached.
+     *
+     * <p>This error may be considered similar to {@link LimitExceededException}.
+     */
+    public static final int STATUS_SERVER_RATE_LIMIT_REACHED = 17;
+
+    /**
+     * Consent notification has not been displayed yet. AdServices is not available.
+     *
+     * <p>This error may be considered similar to {@link IllegalStateException}.
+     */
+    public static final int STATUS_USER_CONSENT_NOTIFICATION_NOT_DISPLAYED_YET = 18;
+
+    /**
+     * Result code for Encryption related failures.
+     *
+     * <p>This error may be considered similar to {@link IllegalArgumentException}.
+     */
+    public static final int STATUS_ENCRYPTION_FAILURE = 19;
+
+    /**
+     * The caller is not authorized to make this call because the package is not in the allowlist.
+     *
+     * <p>This error may be considered similar to {@link SecurityException}.
+     */
+    public static final int STATUS_CALLER_NOT_ALLOWED_PACKAGE_NOT_IN_ALLOWLIST = 20;
+
+    /**
+     * The caller is not authorized to make this call because the package is not in the allowlist.
+     *
+     * <p>This error may be considered similar to {@link SecurityException}.
+     */
+    public static final int STATUS_CALLER_NOT_ALLOWED_PACKAGE_BLOCKLISTED = 21;
+
+    /**
+     * The caller is not authorized to make this call because enrollment data can't be found.
+     *
+     * <p>This error may be considered similar to {@link SecurityException}.
+     */
+    public static final int STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_MATCH_NOT_FOUND = 22;
+
+    /**
+     * The caller is not authorized to make this call because enrollment ID is invalid.
+     *
+     * <p>This error may be considered similar to {@link SecurityException}.
+     */
+    public static final int STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_INVALID_ID = 23;
+
+    /**
+     * The caller is not authorized to make this call because enrollment ID is in the blocklist.
+     *
+     * <p>This error may be considered similar to {@link SecurityException}.
+     */
+    public static final int STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_BLOCKLISTED = 24;
+
+    /**
+     * The caller is not authorized to make this call because permission was not requested in the
+     * manifest.
+     *
+     * <p>This error may be considered similar to {@link SecurityException}.
+     */
+    public static final int STATUS_CALLER_NOT_ALLOWED_MANIFEST_ADSERVICES_CONFIG_NO_PERMISSION = 25;
+
+    /**
+     * AdServices activity is disabled.
+     *
+     * <p>This error may be considered similar to {@link IllegalStateException}.
+     */
+    public static final int STATUS_ADSERVICES_ACTIVITY_DISABLED = 26;
+
+    /**
+     * Callback is shut down and encountered an error when invoking its methods.
+     *
+     * <p>This error may be considered similar to {@link IllegalStateException}.
+     */
+    public static final int STATUS_CALLBACK_SHUTDOWN = 27;
+
+    /** The error message to be returned along with {@link LimitExceededException}. */
+    public static final String RATE_LIMIT_REACHED_ERROR_MESSAGE = "API rate limit exceeded.";
+
+    /** The error message to be returned along with {@link LimitExceededException}. */
+    public static final String SERVER_RATE_LIMIT_REACHED_ERROR_MESSAGE =
+            "Server rate limit exceeded.";
+
+    /**
+     * The error message to be returned along with {@link SecurityException} when permission was not
+     * requested in the manifest.
+     */
+    public static final String SECURITY_EXCEPTION_PERMISSION_NOT_REQUESTED_ERROR_MESSAGE =
+            "Caller is not authorized to call this API. Permission was not requested.";
+
+    /**
+     * The error message to be returned along with {@link SecurityException} when caller is not
+     * allowed to call AdServices (not present in the allowed list).
+     */
+    public static final String SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE =
+            "Caller is not authorized to call this API. Caller is not allowed.";
+
+    /**
+     * The error message to be returned along with {@link SecurityException} when call was executed
+     * from the background thread.
+     */
+    public static final String ILLEGAL_STATE_BACKGROUND_CALLER_ERROR_MESSAGE =
+            "Background thread is not allowed to call this service.";
+
+    /**
+     * The error message to be returned along with {@link IllegalStateException} when call failed
+     * because AdServices activity is disabled.
+     */
+    public static final String ILLEGAL_STATE_ACTIVITY_DISABLED_ERROR_MESSAGE =
+            "AdServices activity is disabled.";
+
+    /**
+     * The error message to be returned along with {@link SecurityException} when call failed
+     * because it crosses user boundaries.
+     */
+    public static final String SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_TO_CROSS_USER_BOUNDARIES =
+            "Caller is not authorized to access information from another user";
+
+    /**
+     * The error message to be returned along with {@link SecurityException} when caller not allowed
+     * to perform this operation on behalf of the given package.
+     */
+    public static final String SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ON_BEHALF_ERROR_MESSAGE =
+            "Caller is not allowed to perform this operation on behalf of the given package.";
+
+    /** The error message to be returned along with {@link TimeoutException}. */
+    public static final String TIMED_OUT_ERROR_MESSAGE = "API timed out.";
+
+    /** The error message to be returned along with {@link InvalidObjectException}. */
+    public static final String INVALID_OBJECT_ERROR_MESSAGE =
+            "The service received an invalid object from the server.";
+
+    /** The error message to be returned along with {@link IllegalArgumentException}. */
+    public static final String ENCRYPTION_FAILURE_MESSAGE = "Failed to encrypt responses.";
+
+    /** Returns true for a successful status. */
+    public static boolean isSuccess(@StatusCode int statusCode) {
+        return statusCode == STATUS_SUCCESS;
+    }
+
+    /** Converts the input {@code statusCode} to an exception to be used in the callback. */
+    @NonNull
+    public static Exception asException(@StatusCode int statusCode) {
+        switch (statusCode) {
+            case STATUS_ENCRYPTION_FAILURE:
+                return new IllegalArgumentException(ENCRYPTION_FAILURE_MESSAGE);
+            case STATUS_INVALID_ARGUMENT:
+                return new IllegalArgumentException();
+            case STATUS_IO_ERROR:
+                return new IOException();
+            case STATUS_KILLSWITCH_ENABLED: // Intentional fallthrough
+            case STATUS_USER_CONSENT_NOTIFICATION_NOT_DISPLAYED_YET: // Intentional fallthrough
+            case STATUS_USER_CONSENT_REVOKED: // Intentional fallthrough
+            case STATUS_JS_SANDBOX_UNAVAILABLE:
+                return new ServiceUnavailableException();
+            case STATUS_PERMISSION_NOT_REQUESTED:
+                return new SecurityException(
+                        SECURITY_EXCEPTION_PERMISSION_NOT_REQUESTED_ERROR_MESSAGE);
+            case STATUS_CALLER_NOT_ALLOWED:
+                return new SecurityException(SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE);
+            case STATUS_CALLER_NOT_ALLOWED_PACKAGE_NOT_IN_ALLOWLIST:
+                return new SecurityException(SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE);
+            case STATUS_CALLER_NOT_ALLOWED_PACKAGE_BLOCKLISTED:
+                return new SecurityException(SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE);
+            case STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_MATCH_NOT_FOUND:
+                return new SecurityException(SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE);
+            case STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_INVALID_ID:
+                return new SecurityException(SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE);
+            case STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_BLOCKLISTED:
+                return new SecurityException(SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE);
+            case STATUS_CALLER_NOT_ALLOWED_MANIFEST_ADSERVICES_CONFIG_NO_PERMISSION:
+                return new SecurityException(SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE);
+            case STATUS_BACKGROUND_CALLER:
+                return new IllegalStateException(ILLEGAL_STATE_BACKGROUND_CALLER_ERROR_MESSAGE);
+            case STATUS_ADSERVICES_ACTIVITY_DISABLED:
+                return new IllegalStateException(ILLEGAL_STATE_ACTIVITY_DISABLED_ERROR_MESSAGE);
+            case STATUS_UNAUTHORIZED:
+                return new SecurityException(
+                        SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ON_BEHALF_ERROR_MESSAGE);
+            case STATUS_TIMEOUT:
+                return new TimeoutException(TIMED_OUT_ERROR_MESSAGE);
+            case STATUS_RATE_LIMIT_REACHED:
+                return new LimitExceededException(RATE_LIMIT_REACHED_ERROR_MESSAGE);
+            case STATUS_INVALID_OBJECT:
+                return new InvalidObjectException(INVALID_OBJECT_ERROR_MESSAGE);
+            case STATUS_SERVER_RATE_LIMIT_REACHED:
+                return new LimitExceededException(SERVER_RATE_LIMIT_REACHED_ERROR_MESSAGE);
+            default:
+                return new IllegalStateException();
+        }
+    }
+
+    /** Converts the {@link AdServicesResponse} to an exception to be used in the callback. */
+    // TODO(b/328601595): Add unit test for AdServicesStatusUtils.asException
+    @NonNull
+    public static Exception asException(@NonNull AdServicesResponse adServicesResponse) {
+        return asException(adServicesResponse.getStatusCode());
+    }
+
+    /**
+     * Result codes that are common across various APIs.
+     *
+     * @hide
+     */
+    @IntDef(
+            prefix = {"STATUS_"},
+            value = {
+                STATUS_UNSET,
+                STATUS_SUCCESS,
+                STATUS_INTERNAL_ERROR,
+                STATUS_INVALID_ARGUMENT,
+                STATUS_RATE_LIMIT_REACHED,
+                STATUS_UNKNOWN_ERROR,
+                STATUS_IO_ERROR,
+                STATUS_KILLSWITCH_ENABLED,
+                STATUS_USER_CONSENT_REVOKED,
+                STATUS_ADSERVICES_DISABLED,
+                STATUS_ADSERVICES_ACTIVITY_DISABLED,
+                STATUS_PERMISSION_NOT_REQUESTED,
+                STATUS_CALLER_NOT_ALLOWED,
+                STATUS_BACKGROUND_CALLER,
+                STATUS_UNAUTHORIZED,
+                STATUS_TIMEOUT,
+                STATUS_JS_SANDBOX_UNAVAILABLE,
+                STATUS_INVALID_OBJECT,
+                STATUS_SERVER_RATE_LIMIT_REACHED,
+                STATUS_USER_CONSENT_NOTIFICATION_NOT_DISPLAYED_YET,
+                STATUS_ENCRYPTION_FAILURE,
+                STATUS_CALLER_NOT_ALLOWED_PACKAGE_NOT_IN_ALLOWLIST,
+                STATUS_CALLER_NOT_ALLOWED_PACKAGE_BLOCKLISTED,
+                STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_MATCH_NOT_FOUND,
+                STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_INVALID_ID,
+                STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_BLOCKLISTED,
+                STATUS_CALLER_NOT_ALLOWED_MANIFEST_ADSERVICES_CONFIG_NO_PERMISSION,
+                STATUS_CALLBACK_SHUTDOWN
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StatusCode {}
+
+    private AdServicesStatusUtils() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/android-35/android/adservices/common/AdTechIdentifier.java b/android-35/android/adservices/common/AdTechIdentifier.java
new file mode 100644
index 0000000..e7fe66c
--- /dev/null
+++ b/android-35/android/adservices/common/AdTechIdentifier.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.common;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/** An Identifier representing an ad buyer or seller. */
+public final class AdTechIdentifier implements Parcelable {
+
+    @NonNull private final String mIdentifier;
+
+    private AdTechIdentifier(@NonNull Parcel in) {
+        this(in.readString());
+    }
+
+    private AdTechIdentifier(@NonNull String adTechIdentifier) {
+        this(adTechIdentifier, true);
+    }
+
+    private AdTechIdentifier(@NonNull String adTechIdentifier, boolean validate) {
+        Objects.requireNonNull(adTechIdentifier);
+        if (validate) {
+            validate(adTechIdentifier);
+        }
+        mIdentifier = adTechIdentifier;
+    }
+
+    @NonNull
+    public static final Creator<AdTechIdentifier> CREATOR =
+            new Creator<AdTechIdentifier>() {
+                @Override
+                public AdTechIdentifier createFromParcel(Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new AdTechIdentifier(in);
+                }
+
+                @Override
+                public AdTechIdentifier[] newArray(int size) {
+                    return new AdTechIdentifier[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+        dest.writeString(mIdentifier);
+    }
+
+    /**
+     * Compares this AdTechIdentifier to the specified object. The result is true if and only if the
+     * argument is not null and is a AdTechIdentifier object with the same string form (obtained by
+     * calling {@link #toString()}). Note that this method will not perform any eTLD+1 normalization
+     * so two AdTechIdentifier objects with the same eTLD+1 could be not equal if the String
+     * representations of the objects was not equal.
+     *
+     * @param o The object to compare this AdTechIdentifier against
+     * @return true if the given object represents an AdTechIdentifier equivalent to this
+     *     AdTechIdentifier, false otherwise
+     */
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof AdTechIdentifier
+                && mIdentifier.equals(((AdTechIdentifier) o).toString());
+    }
+
+    /**
+     * Returns a hash code corresponding to the string representation of this class obtained by
+     * calling {@link #toString()}. Note that this method will not perform any eTLD+1 normalization
+     * so two AdTechIdentifier objects with the same eTLD+1 could have different hash codes if the
+     * underlying string representation was different.
+     *
+     * @return a hash code value for this object.
+     */
+    @Override
+    public int hashCode() {
+        return mIdentifier.hashCode();
+    }
+
+    /** @return The identifier in String form. */
+    @Override
+    @NonNull
+    public String toString() {
+        return mIdentifier;
+    }
+
+    /**
+     * Construct an instance of this class from a String.
+     *
+     * @param source A valid eTLD+1 domain of an ad buyer or seller or null.
+     * @return An {@link AdTechIdentifier} class wrapping the given domain or null if the input was
+     *     null.
+     */
+    @NonNull
+    public static AdTechIdentifier fromString(@NonNull String source) {
+        return AdTechIdentifier.fromString(source, true);
+    }
+
+    /**
+     * Construct an instance of this class from a String.
+     *
+     * @param source A valid eTLD+1 domain of an ad buyer or seller.
+     * @param validate Construction-time validation is run on the string if and only if this is
+     *     true.
+     * @return An {@link AdTechIdentifier} class wrapping the given domain.
+     * @hide
+     */
+    @NonNull
+    public static AdTechIdentifier fromString(@NonNull String source, boolean validate) {
+        return new AdTechIdentifier(source, validate);
+    }
+
+    private void validate(String inputString) {
+        // TODO(b/238849930) Bring existing validation function here
+    }
+}
diff --git a/android-35/android/adservices/common/AppInstallFilters.java b/android-35/android/adservices/common/AppInstallFilters.java
new file mode 100644
index 0000000..02b344b
--- /dev/null
+++ b/android-35/android/adservices/common/AppInstallFilters.java
@@ -0,0 +1,206 @@
+/*
+ * 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 android.adservices.common;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.AdServicesParcelableUtil;
+import com.android.adservices.flags.Flags;
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+// TODO(b/266837113) link to setAppInstallAdvertisers once unhidden.
+
+/**
+ * A container for the ad filters that are based on app install state.
+ *
+ * <p>App install filters filter out ads based on the presence of packages installed on the device.
+ * In order for filtering to work, a package must call the setAppInstallAdvertisers API with the
+ * identifier of the adtech who owns this ad. If that call has been made, and the ad contains an
+ * {@link AppInstallFilters} object whose package name set contains the name of the package, the ad
+ * will be removed from the auction.
+ *
+ * <p>Note that the filtering is based on any package with one of the listed package names being on
+ * the device. It is possible that the package holding the package name is not the application
+ * targeted by the ad.
+ */
+@FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
+public final class AppInstallFilters implements Parcelable {
+    /** @hide */
+    @VisibleForTesting public static final String PACKAGE_NAMES_FIELD_NAME = "package_names";
+
+    @NonNull private final Set<String> mPackageNames;
+
+    @NonNull
+    public static final Creator<AppInstallFilters> CREATOR =
+            new Creator<AppInstallFilters>() {
+                @NonNull
+                @Override
+                public AppInstallFilters createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new AppInstallFilters(in);
+                }
+
+                @NonNull
+                @Override
+                public AppInstallFilters[] newArray(int size) {
+                    return new AppInstallFilters[size];
+                }
+            };
+
+    private AppInstallFilters(@NonNull Builder builder) {
+        Objects.requireNonNull(builder);
+
+        mPackageNames = builder.mPackageNames;
+    }
+
+    private AppInstallFilters(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        mPackageNames = AdServicesParcelableUtil.readStringSetFromParcel(in);
+    }
+
+    /**
+     * Gets the list of package names this ad is filtered on.
+     *
+     * <p>The ad containing this filter will be removed from the ad auction if any of the package
+     * names are present on the device and have called setAppInstallAdvertisers.
+     */
+    @NonNull
+    public Set<String> getPackageNames() {
+        return mPackageNames;
+    }
+
+    /**
+     * @return The estimated size of this object, in bytes using UTF_8 encoding.
+     * @hide
+     */
+    public int getSizeInBytes() {
+        int totalSize = 0;
+        for (String packageName : mPackageNames) {
+            totalSize += packageName.getBytes(StandardCharsets.UTF_8).length;
+        }
+        return totalSize;
+    }
+
+    /**
+     * A JSON serializer.
+     *
+     * @return A JSON serialization of this object.
+     * @hide
+     */
+    public JSONObject toJson() throws JSONException {
+        JSONObject toReturn = new JSONObject();
+        JSONArray packageNames = new JSONArray();
+        for (String packageName : mPackageNames) {
+            packageNames.put(packageName);
+        }
+        toReturn.put(PACKAGE_NAMES_FIELD_NAME, packageNames);
+        return toReturn;
+    }
+
+    /**
+     * A JSON de-serializer.
+     *
+     * @param json A JSON representation of an {@link AppInstallFilters} object as would be
+     *     generated by {@link #toJson()}.
+     * @return An {@link AppInstallFilters} object generated from the given JSON.
+     * @hide
+     */
+    public static AppInstallFilters fromJson(JSONObject json) throws JSONException {
+        JSONArray serializedPackageNames = json.getJSONArray(PACKAGE_NAMES_FIELD_NAME);
+        Set<String> packageNames = new HashSet<>();
+        for (int i = 0; i < serializedPackageNames.length(); i++) {
+            Object packageName = serializedPackageNames.get(i);
+            if (packageName instanceof String) {
+                packageNames.add((String) packageName);
+            } else {
+                throw new JSONException(
+                        "Found non-string package name when de-serializing AppInstallFilters");
+            }
+        }
+        return new Builder().setPackageNames(packageNames).build();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+        AdServicesParcelableUtil.writeStringSetToParcel(dest, mPackageNames);
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Checks whether the {@link AppInstallFilters} objects contain the same information. */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AppInstallFilters)) return false;
+        AppInstallFilters that = (AppInstallFilters) o;
+        return mPackageNames.equals(that.mPackageNames);
+    }
+
+    /** Returns the hash of the {@link AppInstallFilters} object's data. */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPackageNames);
+    }
+
+    @Override
+    public String toString() {
+        return "AppInstallFilters{" + "mPackageNames=" + mPackageNames + '}';
+    }
+
+    /** Builder for creating {@link AppInstallFilters} objects. */
+    public static final class Builder {
+        @NonNull private Set<String> mPackageNames = new HashSet<>();
+
+        public Builder() {}
+
+        /**
+         * Gets the list of package names this ad is filtered on.
+         *
+         * <p>See {@link #getPackageNames()} for more information.
+         */
+        @NonNull
+        public Builder setPackageNames(@NonNull Set<String> packageNames) {
+            Objects.requireNonNull(packageNames);
+            mPackageNames = packageNames;
+            return this;
+        }
+
+        /** Builds and returns a {@link AppInstallFilters} instance. */
+        @NonNull
+        public AppInstallFilters build() {
+            return new AppInstallFilters(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/common/AssetFileDescriptorUtil.java b/android-35/android/adservices/common/AssetFileDescriptorUtil.java
new file mode 100644
index 0000000..fe56fbe
--- /dev/null
+++ b/android-35/android/adservices/common/AssetFileDescriptorUtil.java
@@ -0,0 +1,88 @@
+/*
+ * 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 android.adservices.common;
+
+import android.annotation.NonNull;
+import android.content.res.AssetFileDescriptor;
+import android.os.ParcelFileDescriptor;
+
+import com.android.adservices.LoggerFactory;
+
+import java.io.DataInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * Utility class used to set up the read and write pipes for the usage of reading pointers from
+ * shared memory.
+ *
+ * @hide
+ */
+public class AssetFileDescriptorUtil {
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger();
+
+    private AssetFileDescriptorUtil() {}
+
+    /**
+     * Creates a read and write pipe, writes the data in {@code buffer} into the write end, and
+     * returns the read end in the form of a {@link AssetFileDescriptor}.
+     *
+     * @throws IOException if an exception is encountered while creating or writing to the pipe
+     */
+    public static AssetFileDescriptor setupAssetFileDescriptorResponse(
+            @NonNull byte[] buffer, @NonNull ExecutorService executorService) throws IOException {
+        Objects.requireNonNull(buffer);
+        Objects.requireNonNull(executorService);
+
+        ParcelFileDescriptor[] descriptors = ParcelFileDescriptor.createPipe();
+        ParcelFileDescriptor writeDescriptor = descriptors[1];
+
+        executorService.execute(
+                () -> {
+                    try (FileOutputStream outputStream =
+                            new FileOutputStream(writeDescriptor.getFileDescriptor())) {
+                        outputStream.write(buffer);
+                    } catch (IOException e) {
+                        sLogger.e(
+                                e, "Encountered IO Exception while writing byte array to stream.");
+                    }
+                });
+        return new AssetFileDescriptor(descriptors[0], 0, buffer.length);
+    }
+
+    /**
+     * Reads the content the {@link AssetFileDescriptor} points to into a buffer and returns the
+     * number of bytes read.
+     *
+     * @throws IOException if an exception is encountered while reading the content.
+     */
+    public static byte[] readAssetFileDescriptorIntoBuffer(
+            @NonNull AssetFileDescriptor assetFileDescriptor) throws IOException {
+        Objects.requireNonNull(assetFileDescriptor);
+
+        byte[] result = new byte[(int) assetFileDescriptor.getLength()];
+
+        try (DataInputStream inputStream =
+                new DataInputStream(assetFileDescriptor.createInputStream())) {
+            inputStream.readFully(result);
+        }
+
+        return result;
+    }
+}
diff --git a/android-35/android/adservices/common/CallerMetadata.java b/android-35/android/adservices/common/CallerMetadata.java
new file mode 100644
index 0000000..4c3be11
--- /dev/null
+++ b/android-35/android/adservices/common/CallerMetadata.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.common;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A class to hold the metadata of an IPC call.
+ *
+ * @hide
+ */
+public class CallerMetadata implements Parcelable {
+    private @NonNull long mBinderElapsedTimestamp;
+
+    private CallerMetadata(@NonNull long binderElapsedTimestamp) {
+        mBinderElapsedTimestamp = binderElapsedTimestamp;
+    }
+
+    private CallerMetadata(@NonNull Parcel in) {
+        mBinderElapsedTimestamp = in.readLong();
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<CallerMetadata> CREATOR =
+            new Parcelable.Creator<CallerMetadata>() {
+                @Override
+                public CallerMetadata createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new CallerMetadata(in);
+                }
+
+                @Override
+                public CallerMetadata[] newArray(int size) {
+                    return new CallerMetadata[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeLong(mBinderElapsedTimestamp);
+    }
+
+    /** Get the binder elapsed timestamp. */
+    public long getBinderElapsedTimestamp() {
+        return mBinderElapsedTimestamp;
+    }
+
+    /** Builder for {@link CallerMetadata} objects. */
+    public static final class Builder {
+        private long mBinderElapsedTimestamp;
+
+        public Builder() {
+        }
+
+        /** Set the binder elapsed timestamp. */
+        public @NonNull CallerMetadata.Builder setBinderElapsedTimestamp(
+                @NonNull long binderElapsedTimestamp) {
+            mBinderElapsedTimestamp = binderElapsedTimestamp;
+            return this;
+        }
+
+        /** Builds a {@link CallerMetadata} instance. */
+        public @NonNull CallerMetadata build() {
+            return new CallerMetadata(mBinderElapsedTimestamp);
+        }
+    }
+}
diff --git a/android-35/android/adservices/common/ConsentStatus.java b/android-35/android/adservices/common/ConsentStatus.java
new file mode 100644
index 0000000..2fc6b38
--- /dev/null
+++ b/android-35/android/adservices/common/ConsentStatus.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 android.adservices.common;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Utility class containing consent status codes.
+ *
+ * <p>Those status codes are internal only.
+ *
+ * @hide
+ */
+public class ConsentStatus {
+    public static final int UNKNOWN = 0;
+    public static final int UNSET = 1;
+    public static final int REVOKED = 2;
+    public static final int GIVEN = 3;
+    public static final int SERVICE_NOT_ENABLED = 4;
+    public static final int WAS_RESET = 5;
+
+    /**
+     * Result codes that are common across various APIs.
+     *
+     * @hide
+     */
+    @IntDef(
+            prefix = {""},
+            value = {UNKNOWN, UNSET, REVOKED, GIVEN, SERVICE_NOT_ENABLED, WAS_RESET})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ConsentStatusCode {}
+}
diff --git a/android-35/android/adservices/common/EnableAdServicesResponse.java b/android-35/android/adservices/common/EnableAdServicesResponse.java
new file mode 100644
index 0000000..ab87b32
--- /dev/null
+++ b/android-35/android/adservices/common/EnableAdServicesResponse.java
@@ -0,0 +1,190 @@
+/*
+ * 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 android.adservices.common;
+
+import static android.adservices.common.AdServicesStatusUtils.StatusCode;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+
+/**
+ * Response parcel of the enableAdServices API.
+ *
+ * @hide
+ */
+@SystemApi
+public final class EnableAdServicesResponse implements Parcelable {
+
+    private int mStatusCode;
+
+    private String mErrorMessage;
+
+    private boolean mIsSuccess;
+
+    private boolean mIsApiEnabled;
+
+    private EnableAdServicesResponse(
+            @StatusCode int statusCode,
+            @Nullable String errorMessage,
+            boolean isSuccess,
+            boolean isApiEnabled) {
+        mStatusCode = statusCode;
+        mErrorMessage = errorMessage;
+        mIsSuccess = isSuccess;
+        mIsApiEnabled = isApiEnabled;
+    }
+
+    private EnableAdServicesResponse(@NonNull Parcel in) {
+        mStatusCode = in.readInt();
+        mErrorMessage = in.readString();
+        mIsSuccess = in.readBoolean();
+        mIsApiEnabled = in.readBoolean();
+    }
+
+    /** Returns the response status code. */
+    int getStatusCode() {
+        return mStatusCode;
+    }
+
+    /** Returns whether the enableAdServices API finished successfully. */
+    public boolean isSuccess() {
+        return mIsSuccess;
+    }
+
+    /** Returns whether the enableAdServices API is enabled. */
+    public boolean isApiEnabled() {
+        return mIsApiEnabled;
+    }
+
+    @NonNull
+    public static final Creator<EnableAdServicesResponse> CREATOR =
+            new Parcelable.Creator<EnableAdServicesResponse>() {
+                @Override
+                public EnableAdServicesResponse createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new EnableAdServicesResponse(in);
+                }
+
+                @Override
+                public EnableAdServicesResponse[] newArray(int size) {
+                    return new EnableAdServicesResponse[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        dest.writeInt(mStatusCode);
+        dest.writeString(mErrorMessage);
+        dest.writeBoolean(mIsSuccess);
+        dest.writeBoolean(mIsApiEnabled);
+    }
+
+    @Override
+    public String toString() {
+        return "EnableAdServicesResponse{"
+                + "mStatusCode="
+                + mStatusCode
+                + ", mErrorMessage="
+                + mErrorMessage
+                + ", mIsSuccess="
+                + mIsSuccess
+                + ", mIsApiEnabled="
+                + mIsApiEnabled
+                + "'}";
+    }
+
+    /**
+     * Builder for {@link EnableAdServicesResponse} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        @StatusCode
+        private int mStatusCode = AdServicesStatusUtils.STATUS_UNSET;
+
+        @Nullable
+        private String mErrorMessage;
+
+        private boolean mIsSuccess;
+
+        private boolean mIsApiEnabled;
+
+        public Builder() {
+        }
+
+        /** Set the enableAdServices API response status Code. */
+        @NonNull
+        public EnableAdServicesResponse.Builder setStatusCode(
+                @AdServicesStatusUtils.StatusCode int statusCode) {
+            mStatusCode = statusCode;
+            return this;
+        }
+
+        /** Set the error messaged passed by the enableAdServices API. */
+        @NonNull
+        public EnableAdServicesResponse.Builder setErrorMessage(@Nullable String errorMessage) {
+            mErrorMessage = errorMessage;
+            return this;
+        }
+
+        /** Set the isSuccess bit when enableAdServices API finishes successfully. */
+        @NonNull
+        public EnableAdServicesResponse.Builder setSuccess(boolean isSuccess) {
+            mIsSuccess = isSuccess;
+            return this;
+        }
+
+        /** Set the isApiEnabled bit when enableAdServices API is enabled. */
+        @NonNull
+        public EnableAdServicesResponse.Builder setApiEnabled(boolean isApiEnabled) {
+            mIsApiEnabled = isApiEnabled;
+            return this;
+        }
+
+        /**
+         * Builds a {@link EnableAdServicesResponse} instance.
+         *
+         * <p>throws IllegalArgumentException if any of the status code is null or error message is
+         * not set for an unsuccessful status.
+         */
+        @NonNull
+        public EnableAdServicesResponse build() {
+            Preconditions.checkArgument(
+                    mStatusCode != AdServicesStatusUtils.STATUS_UNSET,
+                    "Status code has not been set!");
+
+            return new EnableAdServicesResponse(
+                    mStatusCode, mErrorMessage, mIsSuccess, mIsApiEnabled);
+        }
+    }
+}
diff --git a/android-35/android/adservices/common/FledgeErrorResponse.java b/android-35/android/adservices/common/FledgeErrorResponse.java
new file mode 100644
index 0000000..10274d6
--- /dev/null
+++ b/android-35/android/adservices/common/FledgeErrorResponse.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.common;
+
+import android.adservices.common.AdServicesStatusUtils.StatusCode;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Represent a generic response for FLEDGE API's.
+ *
+ * @hide
+ */
+public final class FledgeErrorResponse extends AdServicesResponse {
+
+    private FledgeErrorResponse(@StatusCode int statusCode, @Nullable String errorMessage) {
+        super(statusCode, errorMessage);
+    }
+
+    private FledgeErrorResponse(@NonNull Parcel in) {
+        super(in);
+    }
+
+    @NonNull
+    public static final Creator<FledgeErrorResponse> CREATOR =
+            new Parcelable.Creator<FledgeErrorResponse>() {
+                @Override
+                public FledgeErrorResponse createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new FledgeErrorResponse(in);
+                }
+
+                @Override
+                public FledgeErrorResponse[] newArray(int size) {
+                    return new FledgeErrorResponse[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        dest.writeInt(mStatusCode);
+        dest.writeString(mErrorMessage);
+    }
+
+    @Override
+    public String toString() {
+        return "FledgeErrorResponse{"
+                + "mStatusCode="
+                + mStatusCode
+                + ", mErrorMessage='"
+                + mErrorMessage
+                + "'}";
+    }
+
+    /**
+     * Builder for {@link FledgeErrorResponse} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        @StatusCode private int mStatusCode = AdServicesStatusUtils.STATUS_UNSET;
+        @Nullable private String mErrorMessage;
+
+        public Builder() {}
+
+        /** Set the Status Code. */
+        @NonNull
+        public FledgeErrorResponse.Builder setStatusCode(@StatusCode int statusCode) {
+            mStatusCode = statusCode;
+            return this;
+        }
+
+        /** Set the Error Message. */
+        @NonNull
+        public FledgeErrorResponse.Builder setErrorMessage(@Nullable String errorMessage) {
+            mErrorMessage = errorMessage;
+            return this;
+        }
+
+        /**
+         * Builds a {@link FledgeErrorResponse} instance.
+         *
+         * <p>throws IllegalArgumentException if any of the status code is null or error message is
+         * not set for an unsuccessful status
+         */
+        @NonNull
+        public FledgeErrorResponse build() {
+            Preconditions.checkArgument(
+                    mStatusCode != AdServicesStatusUtils.STATUS_UNSET,
+                    "Status code has not been set!");
+
+            return new FledgeErrorResponse(mStatusCode, mErrorMessage);
+        }
+    }
+}
diff --git a/android-35/android/adservices/common/FrequencyCapFilters.java b/android-35/android/adservices/common/FrequencyCapFilters.java
new file mode 100644
index 0000000..f940cf9
--- /dev/null
+++ b/android-35/android/adservices/common/FrequencyCapFilters.java
@@ -0,0 +1,470 @@
+/*
+ * 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 android.adservices.common;
+
+import android.adservices.adselection.ReportImpressionRequest;
+import android.adservices.adselection.UpdateAdCounterHistogramRequest;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.OutcomeReceiver;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * A container for the ad filters that are based on frequency caps.
+ *
+ * <p>No more than 20 frequency cap filters may be associated with a single ad.
+ *
+ * <p>Frequency caps filters combine an event type with a list of {@link KeyedFrequencyCap} objects
+ * to define a collection of ad filters. If any of these frequency caps are exceeded for a given ad,
+ * the ad will be removed from the group of ads submitted to a buyer adtech's bidding function.
+ */
+public final class FrequencyCapFilters implements Parcelable {
+    /** @hide */
+    public static final String NUM_FREQUENCY_CAP_FILTERS_EXCEEDED_FORMAT =
+            "FrequencyCapFilters should have no more than %d filters";
+    /** @hide */
+    public static final int MAX_NUM_FREQUENCY_CAP_FILTERS = 20;
+    /** @hide */
+    public static final String FREQUENCY_CAP_FILTERS_NULL_LIST_ERROR_MESSAGE =
+            "FrequencyCapFilters should not set null list of KeyedFrequencyCaps";
+    /** @hide */
+    public static final String FREQUENCY_CAP_FILTERS_NULL_ELEMENT_ERROR_MESSAGE =
+            "FrequencyCapFilters should not contain null KeyedFrequencyCaps";
+
+    /**
+     * Event types which are used to update ad counter histograms, which inform frequency cap
+     * filtering in Protected Audience.
+     *
+     * @hide
+     */
+    @IntDef(
+            prefix = {"AD_EVENT_TYPE_"},
+            value = {
+                AD_EVENT_TYPE_INVALID,
+                AD_EVENT_TYPE_WIN,
+                AD_EVENT_TYPE_IMPRESSION,
+                AD_EVENT_TYPE_VIEW,
+                AD_EVENT_TYPE_CLICK,
+                AD_EVENT_TYPE_MIN,
+                AD_EVENT_TYPE_MAX
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AdEventType {}
+
+    /** @hide */
+    public static final int AD_EVENT_TYPE_INVALID = -1;
+
+    /**
+     * The WIN ad event type is automatically populated within the Protected Audience service for
+     * any winning ad which is returned from Protected Audience ad selection.
+     *
+     * <p>It should not be used to manually update an ad counter histogram.
+     */
+    public static final int AD_EVENT_TYPE_WIN = 0;
+
+    public static final int AD_EVENT_TYPE_IMPRESSION = 1;
+    public static final int AD_EVENT_TYPE_VIEW = 2;
+    public static final int AD_EVENT_TYPE_CLICK = 3;
+
+    /** @hide */
+    public static final int AD_EVENT_TYPE_MIN = AD_EVENT_TYPE_WIN;
+    /** @hide */
+    public static final int AD_EVENT_TYPE_MAX = AD_EVENT_TYPE_CLICK;
+
+    /** @hide */
+    @VisibleForTesting public static final String WIN_EVENTS_FIELD_NAME = "win";
+    /** @hide */
+    @VisibleForTesting public static final String IMPRESSION_EVENTS_FIELD_NAME = "impression";
+    /** @hide */
+    @VisibleForTesting public static final String VIEW_EVENTS_FIELD_NAME = "view";
+    /** @hide */
+    @VisibleForTesting public static final String CLICK_EVENTS_FIELD_NAME = "click";
+
+    @NonNull private final List<KeyedFrequencyCap> mKeyedFrequencyCapsForWinEvents;
+    @NonNull private final List<KeyedFrequencyCap> mKeyedFrequencyCapsForImpressionEvents;
+    @NonNull private final List<KeyedFrequencyCap> mKeyedFrequencyCapsForViewEvents;
+    @NonNull private final List<KeyedFrequencyCap> mKeyedFrequencyCapsForClickEvents;
+
+    @NonNull
+    public static final Creator<FrequencyCapFilters> CREATOR =
+            new Creator<FrequencyCapFilters>() {
+                @Override
+                public FrequencyCapFilters createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new FrequencyCapFilters(in);
+                }
+
+                @Override
+                public FrequencyCapFilters[] newArray(int size) {
+                    return new FrequencyCapFilters[size];
+                }
+            };
+
+    private FrequencyCapFilters(@NonNull Builder builder) {
+        Objects.requireNonNull(builder);
+
+        mKeyedFrequencyCapsForWinEvents = builder.mKeyedFrequencyCapsForWinEvents;
+        mKeyedFrequencyCapsForImpressionEvents = builder.mKeyedFrequencyCapsForImpressionEvents;
+        mKeyedFrequencyCapsForViewEvents = builder.mKeyedFrequencyCapsForViewEvents;
+        mKeyedFrequencyCapsForClickEvents = builder.mKeyedFrequencyCapsForClickEvents;
+    }
+
+    private FrequencyCapFilters(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        mKeyedFrequencyCapsForWinEvents = new ArrayList<>();
+        mKeyedFrequencyCapsForImpressionEvents = new ArrayList<>();
+        mKeyedFrequencyCapsForViewEvents = new ArrayList<>();
+        mKeyedFrequencyCapsForClickEvents = new ArrayList<>();
+
+        in.readTypedList(mKeyedFrequencyCapsForWinEvents, KeyedFrequencyCap.CREATOR);
+        in.readTypedList(mKeyedFrequencyCapsForImpressionEvents, KeyedFrequencyCap.CREATOR);
+        in.readTypedList(mKeyedFrequencyCapsForViewEvents, KeyedFrequencyCap.CREATOR);
+        in.readTypedList(mKeyedFrequencyCapsForClickEvents, KeyedFrequencyCap.CREATOR);
+    }
+
+    /**
+     * Gets the list of {@link KeyedFrequencyCap} objects that will filter on the {@link
+     * #AD_EVENT_TYPE_WIN} event type.
+     *
+     * <p>These frequency caps apply to events for ads that were selected as winners in ad
+     * selection. Winning ads are used to automatically increment the associated counter keys on the
+     * win event type.
+     *
+     * <p>Note that the {@link #AD_EVENT_TYPE_WIN} event type cannot be updated manually using the
+     * {@link android.adservices.adselection.AdSelectionManager#updateAdCounterHistogram(
+     * UpdateAdCounterHistogramRequest, Executor, OutcomeReceiver)} API.
+     */
+    @NonNull
+    public List<KeyedFrequencyCap> getKeyedFrequencyCapsForWinEvents() {
+        return mKeyedFrequencyCapsForWinEvents;
+    }
+
+    /**
+     * Gets the list of {@link KeyedFrequencyCap} objects that will filter on the {@link
+     * #AD_EVENT_TYPE_IMPRESSION} event type.
+     *
+     * <p>These frequency caps apply to events which correlate to an impression as interpreted by an
+     * adtech.
+     *
+     * <p>Note that events are not automatically counted when calling {@link
+     * android.adservices.adselection.AdSelectionManager#reportImpression(ReportImpressionRequest,
+     * Executor, OutcomeReceiver)}. Instead, the {@link #AD_EVENT_TYPE_IMPRESSION} event type must
+     * be updated using the {@link
+     * android.adservices.adselection.AdSelectionManager#updateAdCounterHistogram(
+     * UpdateAdCounterHistogramRequest, Executor, OutcomeReceiver)} API.
+     */
+    @NonNull
+    public List<KeyedFrequencyCap> getKeyedFrequencyCapsForImpressionEvents() {
+        return mKeyedFrequencyCapsForImpressionEvents;
+    }
+
+    /**
+     * Gets the list of {@link KeyedFrequencyCap} objects that will filter on the {@link
+     * #AD_EVENT_TYPE_VIEW} event type.
+     *
+     * <p>These frequency caps apply to events which correlate to a view as interpreted by an
+     * adtech. View events are counted when the {@link
+     * android.adservices.adselection.AdSelectionManager#updateAdCounterHistogram(
+     * UpdateAdCounterHistogramRequest, Executor, OutcomeReceiver)} API is invoked with the {@link
+     * #AD_EVENT_TYPE_VIEW} event type.
+     */
+    @NonNull
+    public List<KeyedFrequencyCap> getKeyedFrequencyCapsForViewEvents() {
+        return mKeyedFrequencyCapsForViewEvents;
+    }
+
+    /**
+     * Gets the list of {@link KeyedFrequencyCap} objects that will filter on the {@link
+     * #AD_EVENT_TYPE_CLICK} event type.
+     *
+     * <p>These frequency caps apply to events which correlate to a click as interpreted by an
+     * adtech. Click events are counted when the {@link
+     * android.adservices.adselection.AdSelectionManager#updateAdCounterHistogram(
+     * UpdateAdCounterHistogramRequest, Executor, OutcomeReceiver)} API is invoked with the {@link
+     * #AD_EVENT_TYPE_CLICK} event type.
+     */
+    @NonNull
+    public List<KeyedFrequencyCap> getKeyedFrequencyCapsForClickEvents() {
+        return mKeyedFrequencyCapsForClickEvents;
+    }
+
+    /**
+     * @return The estimated size of this object, in bytes.
+     * @hide
+     */
+    public int getSizeInBytes() {
+        return getSizeInBytesOfFcapList(mKeyedFrequencyCapsForWinEvents)
+                + getSizeInBytesOfFcapList(mKeyedFrequencyCapsForImpressionEvents)
+                + getSizeInBytesOfFcapList(mKeyedFrequencyCapsForViewEvents)
+                + getSizeInBytesOfFcapList(mKeyedFrequencyCapsForClickEvents);
+    }
+
+    private int getSizeInBytesOfFcapList(List<KeyedFrequencyCap> fcaps) {
+        int toReturn = 0;
+        for (final KeyedFrequencyCap fcap : fcaps) {
+            toReturn += fcap.getSizeInBytes();
+        }
+        return toReturn;
+    }
+
+    /**
+     * A JSON serializer.
+     *
+     * @return A JSON serialization of this object.
+     * @hide
+     */
+    public JSONObject toJson() throws JSONException {
+        JSONObject toReturn = new JSONObject();
+        toReturn.put(WIN_EVENTS_FIELD_NAME, fcapSetToJsonArray(mKeyedFrequencyCapsForWinEvents));
+        toReturn.put(
+                IMPRESSION_EVENTS_FIELD_NAME,
+                fcapSetToJsonArray(mKeyedFrequencyCapsForImpressionEvents));
+        toReturn.put(VIEW_EVENTS_FIELD_NAME, fcapSetToJsonArray(mKeyedFrequencyCapsForViewEvents));
+        toReturn.put(
+                CLICK_EVENTS_FIELD_NAME, fcapSetToJsonArray(mKeyedFrequencyCapsForClickEvents));
+        return toReturn;
+    }
+
+    private static JSONArray fcapSetToJsonArray(List<KeyedFrequencyCap> fcapSet)
+            throws JSONException {
+        JSONArray toReturn = new JSONArray();
+        for (KeyedFrequencyCap fcap : fcapSet) {
+            toReturn.put(fcap.toJson());
+        }
+        return toReturn;
+    }
+
+    /**
+     * A JSON de-serializer.
+     *
+     * @param json A JSON representation of an {@link FrequencyCapFilters} object as would be
+     *     generated by {@link #toJson()}.
+     * @return An {@link FrequencyCapFilters} object generated from the given JSON.
+     * @hide
+     */
+    public static FrequencyCapFilters fromJson(JSONObject json) throws JSONException {
+        Builder builder = new Builder();
+        if (json.has(WIN_EVENTS_FIELD_NAME)) {
+            builder.setKeyedFrequencyCapsForWinEvents(
+                    jsonArrayToFcapList(json.getJSONArray(WIN_EVENTS_FIELD_NAME)));
+        }
+        if (json.has(IMPRESSION_EVENTS_FIELD_NAME)) {
+            builder.setKeyedFrequencyCapsForImpressionEvents(
+                    jsonArrayToFcapList(json.getJSONArray(IMPRESSION_EVENTS_FIELD_NAME)));
+        }
+        if (json.has(VIEW_EVENTS_FIELD_NAME)) {
+            builder.setKeyedFrequencyCapsForViewEvents(
+                    jsonArrayToFcapList(json.getJSONArray(VIEW_EVENTS_FIELD_NAME)));
+        }
+        if (json.has(CLICK_EVENTS_FIELD_NAME)) {
+            builder.setKeyedFrequencyCapsForClickEvents(
+                    jsonArrayToFcapList(json.getJSONArray(CLICK_EVENTS_FIELD_NAME)));
+        }
+        return builder.build();
+    }
+
+    private static List<KeyedFrequencyCap> jsonArrayToFcapList(JSONArray json)
+            throws JSONException {
+        List<KeyedFrequencyCap> toReturn = new ArrayList<>();
+        for (int i = 0; i < json.length(); i++) {
+            toReturn.add(KeyedFrequencyCap.fromJson(json.getJSONObject(i)));
+        }
+        return toReturn;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+        dest.writeTypedList(mKeyedFrequencyCapsForWinEvents);
+        dest.writeTypedList(mKeyedFrequencyCapsForImpressionEvents);
+        dest.writeTypedList(mKeyedFrequencyCapsForViewEvents);
+        dest.writeTypedList(mKeyedFrequencyCapsForClickEvents);
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Checks whether the {@link FrequencyCapFilters} objects contain the same information. */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof FrequencyCapFilters)) return false;
+        FrequencyCapFilters that = (FrequencyCapFilters) o;
+        return mKeyedFrequencyCapsForWinEvents.equals(that.mKeyedFrequencyCapsForWinEvents)
+                && mKeyedFrequencyCapsForImpressionEvents.equals(
+                        that.mKeyedFrequencyCapsForImpressionEvents)
+                && mKeyedFrequencyCapsForViewEvents.equals(that.mKeyedFrequencyCapsForViewEvents)
+                && mKeyedFrequencyCapsForClickEvents.equals(that.mKeyedFrequencyCapsForClickEvents);
+    }
+
+    /** Returns the hash of the {@link FrequencyCapFilters} object's data. */
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mKeyedFrequencyCapsForWinEvents,
+                mKeyedFrequencyCapsForImpressionEvents,
+                mKeyedFrequencyCapsForViewEvents,
+                mKeyedFrequencyCapsForClickEvents);
+    }
+
+    @Override
+    public String toString() {
+        return "FrequencyCapFilters{"
+                + "mKeyedFrequencyCapsForWinEvents="
+                + mKeyedFrequencyCapsForWinEvents
+                + ", mKeyedFrequencyCapsForImpressionEvents="
+                + mKeyedFrequencyCapsForImpressionEvents
+                + ", mKeyedFrequencyCapsForViewEvents="
+                + mKeyedFrequencyCapsForViewEvents
+                + ", mKeyedFrequencyCapsForClickEvents="
+                + mKeyedFrequencyCapsForClickEvents
+                + '}';
+    }
+
+    /** Builder for creating {@link FrequencyCapFilters} objects. */
+    public static final class Builder {
+        @NonNull
+        private List<KeyedFrequencyCap> mKeyedFrequencyCapsForWinEvents = new ArrayList<>();
+
+        @NonNull
+        private List<KeyedFrequencyCap> mKeyedFrequencyCapsForImpressionEvents = new ArrayList<>();
+
+        @NonNull
+        private List<KeyedFrequencyCap> mKeyedFrequencyCapsForViewEvents = new ArrayList<>();
+
+        @NonNull
+        private List<KeyedFrequencyCap> mKeyedFrequencyCapsForClickEvents = new ArrayList<>();
+
+        public Builder() {}
+
+        /**
+         * Sets the list of {@link KeyedFrequencyCap} objects that will filter on the {@link
+         * #AD_EVENT_TYPE_WIN} event type.
+         *
+         * <p>See {@link #getKeyedFrequencyCapsForWinEvents()} for more information.
+         */
+        @NonNull
+        public Builder setKeyedFrequencyCapsForWinEvents(
+                @NonNull List<KeyedFrequencyCap> keyedFrequencyCapsForWinEvents) {
+            Objects.requireNonNull(
+                    keyedFrequencyCapsForWinEvents, FREQUENCY_CAP_FILTERS_NULL_LIST_ERROR_MESSAGE);
+            Preconditions.checkArgument(
+                    !keyedFrequencyCapsForWinEvents.contains(null),
+                    FREQUENCY_CAP_FILTERS_NULL_ELEMENT_ERROR_MESSAGE);
+            mKeyedFrequencyCapsForWinEvents = keyedFrequencyCapsForWinEvents;
+            return this;
+        }
+
+        /**
+         * Sets the list of {@link KeyedFrequencyCap} objects that will filter on the {@link
+         * #AD_EVENT_TYPE_IMPRESSION} event type.
+         *
+         * <p>See {@link #getKeyedFrequencyCapsForImpressionEvents()} for more information.
+         */
+        @NonNull
+        public Builder setKeyedFrequencyCapsForImpressionEvents(
+                @NonNull List<KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents) {
+            Objects.requireNonNull(
+                    keyedFrequencyCapsForImpressionEvents,
+                    FREQUENCY_CAP_FILTERS_NULL_LIST_ERROR_MESSAGE);
+            Preconditions.checkArgument(
+                    !keyedFrequencyCapsForImpressionEvents.contains(null),
+                    FREQUENCY_CAP_FILTERS_NULL_ELEMENT_ERROR_MESSAGE);
+            mKeyedFrequencyCapsForImpressionEvents = keyedFrequencyCapsForImpressionEvents;
+            return this;
+        }
+
+        /**
+         * Sets the list of {@link KeyedFrequencyCap} objects that will filter on the {@link
+         * #AD_EVENT_TYPE_VIEW} event type.
+         *
+         * <p>See {@link #getKeyedFrequencyCapsForViewEvents()} for more information.
+         */
+        @NonNull
+        public Builder setKeyedFrequencyCapsForViewEvents(
+                @NonNull List<KeyedFrequencyCap> keyedFrequencyCapsForViewEvents) {
+            Objects.requireNonNull(
+                    keyedFrequencyCapsForViewEvents, FREQUENCY_CAP_FILTERS_NULL_LIST_ERROR_MESSAGE);
+            Preconditions.checkArgument(
+                    !keyedFrequencyCapsForViewEvents.contains(null),
+                    FREQUENCY_CAP_FILTERS_NULL_ELEMENT_ERROR_MESSAGE);
+            mKeyedFrequencyCapsForViewEvents = keyedFrequencyCapsForViewEvents;
+            return this;
+        }
+
+        /**
+         * Sets the list of {@link KeyedFrequencyCap} objects that will filter on the {@link
+         * #AD_EVENT_TYPE_CLICK} event type.
+         *
+         * <p>See {@link #getKeyedFrequencyCapsForClickEvents()} for more information.
+         */
+        @NonNull
+        public Builder setKeyedFrequencyCapsForClickEvents(
+                @NonNull List<KeyedFrequencyCap> keyedFrequencyCapsForClickEvents) {
+            Objects.requireNonNull(
+                    keyedFrequencyCapsForClickEvents,
+                    FREQUENCY_CAP_FILTERS_NULL_LIST_ERROR_MESSAGE);
+            Preconditions.checkArgument(
+                    !keyedFrequencyCapsForClickEvents.contains(null),
+                    FREQUENCY_CAP_FILTERS_NULL_ELEMENT_ERROR_MESSAGE);
+            mKeyedFrequencyCapsForClickEvents = keyedFrequencyCapsForClickEvents;
+            return this;
+        }
+
+        /**
+         * Builds and returns a {@link FrequencyCapFilters} instance.
+         *
+         * <p>No more than 20 frequency cap filters may be associated with a single ad. If more
+         * total filters than the limit have been set, an {@link IllegalArgumentException} will be
+         * thrown.
+         */
+        @NonNull
+        public FrequencyCapFilters build() {
+            int numFrequencyCapFilters = 0;
+            numFrequencyCapFilters += mKeyedFrequencyCapsForWinEvents.size();
+            numFrequencyCapFilters += mKeyedFrequencyCapsForImpressionEvents.size();
+            numFrequencyCapFilters += mKeyedFrequencyCapsForViewEvents.size();
+            numFrequencyCapFilters += mKeyedFrequencyCapsForClickEvents.size();
+
+            Preconditions.checkArgument(
+                    numFrequencyCapFilters <= MAX_NUM_FREQUENCY_CAP_FILTERS,
+                    NUM_FREQUENCY_CAP_FILTERS_EXCEEDED_FORMAT,
+                    MAX_NUM_FREQUENCY_CAP_FILTERS);
+
+            return new FrequencyCapFilters(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/common/GetAdServicesCommonStatesParams.java b/android-35/android/adservices/common/GetAdServicesCommonStatesParams.java
new file mode 100644
index 0000000..51680ab
--- /dev/null
+++ b/android-35/android/adservices/common/GetAdServicesCommonStatesParams.java
@@ -0,0 +1,115 @@
+/*
+ * 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 android.adservices.common;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represent input params to the getAdservicesCommonStates API.
+ *
+ * @hide
+ */
+public final class GetAdServicesCommonStatesParams implements Parcelable {
+    private final String mSdkPackageName;
+    private final String mAppPackageName;
+
+    private GetAdServicesCommonStatesParams(
+            @Nullable String sdkPackageName, @NonNull String appPackageName) {
+        mSdkPackageName = sdkPackageName;
+        mAppPackageName = appPackageName;
+    }
+
+    private GetAdServicesCommonStatesParams(@NonNull Parcel in) {
+        mSdkPackageName = in.readString();
+        mAppPackageName = in.readString();
+    }
+
+    @NonNull
+    public static final Creator<GetAdServicesCommonStatesParams> CREATOR =
+            new Creator<GetAdServicesCommonStatesParams>() {
+                @Override
+                public GetAdServicesCommonStatesParams createFromParcel(Parcel in) {
+                    return new GetAdServicesCommonStatesParams(in);
+                }
+
+                @Override
+                public GetAdServicesCommonStatesParams[] newArray(int size) {
+                    return new GetAdServicesCommonStatesParams[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeString(mSdkPackageName);
+        out.writeString(mAppPackageName);
+    }
+
+    /** Get the Sdk Package Name. This is the package name in the Manifest. */
+    @NonNull
+    public String getSdkPackageName() {
+        return mSdkPackageName;
+    }
+
+    /** Get the App PackageName. */
+    @NonNull
+    public String getAppPackageName() {
+        return mAppPackageName;
+    }
+
+    /** Builder for {@link GetAdServicesCommonStatesParams} objects. */
+    public static final class Builder {
+        private String mSdkPackageName;
+        private String mAppPackageName;
+
+        public Builder(String appPackageName, String sdkPackageName) {
+            mAppPackageName = appPackageName;
+            mSdkPackageName = sdkPackageName;
+        }
+
+        /**
+         * Set the Sdk Package Name. When the app calls the AdId API directly without using an SDK,
+         * don't set this field.
+         */
+        public @NonNull Builder setSdkPackageName(@NonNull String sdkPackageName) {
+            mSdkPackageName = sdkPackageName;
+            return this;
+        }
+
+        /** Set the App PackageName. */
+        public @NonNull Builder setAppPackageName(@NonNull String appPackageName) {
+            mAppPackageName = appPackageName;
+            return this;
+        }
+
+        /** Builds a {@link GetAdServicesCommonStatesParams} instance. */
+        public @NonNull GetAdServicesCommonStatesParams build() {
+            if (mAppPackageName == null || mAppPackageName.isEmpty()) {
+                throw new IllegalArgumentException("App PackageName must not be empty or null");
+            }
+
+            return new GetAdServicesCommonStatesParams(mSdkPackageName, mAppPackageName);
+        }
+    }
+}
diff --git a/android-35/android/adservices/common/IsAdServicesEnabledResult.java b/android-35/android/adservices/common/IsAdServicesEnabledResult.java
new file mode 100644
index 0000000..a9d8728
--- /dev/null
+++ b/android-35/android/adservices/common/IsAdServicesEnabledResult.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.common;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Result from the isAdServicesEnabled API.
+ *
+ * @hide
+ */
+public final class IsAdServicesEnabledResult implements Parcelable {
+    @Nullable private final String mErrorMessage;
+    private final boolean mAdServicesEnabled;
+
+    private IsAdServicesEnabledResult(@Nullable String errorMessage, @NonNull boolean enabled) {
+        mErrorMessage = errorMessage;
+        mAdServicesEnabled = enabled;
+    }
+
+    private IsAdServicesEnabledResult(@NonNull Parcel in) {
+        mErrorMessage = in.readString();
+        mAdServicesEnabled = in.readBoolean();
+    }
+
+    public static final @NonNull Creator<IsAdServicesEnabledResult> CREATOR =
+            new Creator<IsAdServicesEnabledResult>() {
+                @Override
+                public IsAdServicesEnabledResult createFromParcel(Parcel in) {
+                    return new IsAdServicesEnabledResult(in);
+                }
+
+                @Override
+                public IsAdServicesEnabledResult[] newArray(int size) {
+                    return new IsAdServicesEnabledResult[size];
+                }
+            };
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeString(mErrorMessage);
+        out.writeBoolean(mAdServicesEnabled);
+    }
+
+    /** Returns the error message associated with this result. */
+    @Nullable
+    public String getErrorMessage() {
+        return mErrorMessage;
+    }
+
+    /** Returns the Adservices enabled status. */
+    @NonNull
+    public boolean getAdServicesEnabled() {
+        return mAdServicesEnabled;
+    }
+
+    @Override
+    public String toString() {
+        return "GetAdserviceStatusResult{"
+                + ", mErrorMessage='"
+                + mErrorMessage
+                + ", mAdservicesEnabled="
+                + mAdServicesEnabled
+                + '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof IsAdServicesEnabledResult)) {
+            return false;
+        }
+
+        IsAdServicesEnabledResult that = (IsAdServicesEnabledResult) o;
+
+        return Objects.equals(mErrorMessage, that.mErrorMessage)
+                && mAdServicesEnabled == that.mAdServicesEnabled;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mErrorMessage, mAdServicesEnabled);
+    }
+
+    /**
+     * Builder for {@link IsAdServicesEnabledResult} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        @Nullable private String mErrorMessage;
+        private boolean mAdServicesEnabled;
+
+        public Builder() {}
+
+        /** Set the Error Message. */
+        public @NonNull Builder setErrorMessage(@Nullable String errorMessage) {
+            mErrorMessage = errorMessage;
+            return this;
+        }
+
+        /** Set the list of the returned Status */
+        public @NonNull Builder setAdServicesEnabled(@NonNull boolean adServicesEnabled) {
+            mAdServicesEnabled = adServicesEnabled;
+            return this;
+        }
+
+        /**
+         * Builds a {@link IsAdServicesEnabledResult} instance.
+         *
+         * <p>throws IllegalArgumentException if any of the params are null or there is any mismatch
+         * in the size of ModelVersions and TaxonomyVersions.
+         */
+        public @NonNull IsAdServicesEnabledResult build() {
+            return new IsAdServicesEnabledResult(mErrorMessage, mAdServicesEnabled);
+        }
+    }
+}
diff --git a/android-35/android/adservices/common/KeyedFrequencyCap.java b/android-35/android/adservices/common/KeyedFrequencyCap.java
new file mode 100644
index 0000000..95c42c0
--- /dev/null
+++ b/android-35/android/adservices/common/KeyedFrequencyCap.java
@@ -0,0 +1,295 @@
+/*
+ * 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 android.adservices.common;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.time.Duration;
+import java.util.Objects;
+
+/**
+ * A frequency cap for a specific ad counter key.
+ *
+ * <p>Frequency caps define the maximum rate an event can occur within a given time interval. If the
+ * frequency cap is exceeded, the associated ad will be filtered out of ad selection.
+ */
+public final class KeyedFrequencyCap implements Parcelable {
+    /** @hide */
+    @VisibleForTesting public static final String AD_COUNTER_KEY_FIELD_NAME = "ad_counter_key";
+    /** @hide */
+    @VisibleForTesting public static final String MAX_COUNT_FIELD_NAME = "max_count";
+    /** @hide */
+    @VisibleForTesting public static final String INTERVAL_FIELD_NAME = "interval_in_seconds";
+
+    /** @hide */
+    public static final String MAX_COUNT_NOT_POSITIVE_ERROR_MESSAGE =
+            "KeyedFrequencyCap max count %d must be strictly positive";
+    /** @hide */
+    public static final String INTERVAL_NULL_ERROR_MESSAGE =
+            "KeyedFrequencyCap interval must not be null";
+    /** @hide */
+    public static final String INTERVAL_NOT_POSITIVE_FORMAT =
+            "KeyedFrequencyCap interval %s must be strictly positive";
+    /** @hide */
+    public static final String MAX_INTERVAL_EXCEEDED_FORMAT =
+            "KeyedFrequencyCap interval %s must be no greater than %s";
+    /** @hide */
+    public static final Duration MAX_INTERVAL = Duration.ofDays(100);
+
+    // 4 bytes for the key, 12 bytes for the duration, and 4 for the maxCount
+    private static final int SIZE_OF_FIXED_FIELDS = 20;
+
+    private final int mAdCounterKey;
+    private final int mMaxCount;
+    @NonNull private final Duration mInterval;
+
+    @NonNull
+    public static final Creator<KeyedFrequencyCap> CREATOR =
+            new Creator<KeyedFrequencyCap>() {
+                @Override
+                public KeyedFrequencyCap createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new KeyedFrequencyCap(in);
+                }
+
+                @Override
+                public KeyedFrequencyCap[] newArray(int size) {
+                    return new KeyedFrequencyCap[size];
+                }
+            };
+
+    private KeyedFrequencyCap(@NonNull Builder builder) {
+        Objects.requireNonNull(builder);
+
+        mAdCounterKey = builder.mAdCounterKey;
+        mMaxCount = builder.mMaxCount;
+        mInterval = builder.mInterval;
+    }
+
+    private KeyedFrequencyCap(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        mAdCounterKey = in.readInt();
+        mMaxCount = in.readInt();
+        mInterval = Duration.ofSeconds(in.readLong());
+    }
+
+    /**
+     * Returns the ad counter key that the frequency cap is applied to.
+     *
+     * <p>The ad counter key is defined by an adtech and is an arbitrary numeric identifier which
+     * defines any criteria which may have previously been counted and persisted on the device. If
+     * the on-device count exceeds the maximum count within a certain time interval, the frequency
+     * cap has been exceeded.
+     */
+    @NonNull
+    public int getAdCounterKey() {
+        return mAdCounterKey;
+    }
+
+    /**
+     * Returns the maximum count of event occurrences allowed within a given time interval.
+     *
+     * <p>If there are more events matching the ad counter key and ad event type counted on the
+     * device within the time interval defined by {@link #getInterval()}, the frequency cap has been
+     * exceeded, and the ad will not be eligible for ad selection.
+     *
+     * <p>For example, an ad that specifies a filter for a max count of two within one hour will not
+     * be eligible for ad selection if the event has been counted two or more times within the hour
+     * preceding the ad selection process.
+     */
+    public int getMaxCount() {
+        return mMaxCount;
+    }
+
+    /**
+     * Returns the interval, as a {@link Duration} which will be truncated to the nearest second,
+     * over which the frequency cap is calculated.
+     *
+     * <p>When this frequency cap is computed, the number of persisted events is counted in the most
+     * recent time interval. If the count of previously occurring matching events for an adtech is
+     * greater than the number returned by {@link #getMaxCount()}, the frequency cap has been
+     * exceeded, and the ad will not be eligible for ad selection.
+     */
+    @NonNull
+    public Duration getInterval() {
+        return mInterval;
+    }
+
+    /**
+     * @return The estimated size of this object, in bytes.
+     * @hide
+     */
+    public int getSizeInBytes() {
+        return SIZE_OF_FIXED_FIELDS;
+    }
+
+    /**
+     * A JSON serializer.
+     *
+     * @return A JSON serialization of this object.
+     * @hide
+     */
+    public JSONObject toJson() throws JSONException {
+        JSONObject toReturn = new JSONObject();
+        toReturn.put(AD_COUNTER_KEY_FIELD_NAME, mAdCounterKey);
+        toReturn.put(MAX_COUNT_FIELD_NAME, mMaxCount);
+        toReturn.put(INTERVAL_FIELD_NAME, mInterval.getSeconds());
+        return toReturn;
+    }
+
+    /**
+     * A JSON de-serializer.
+     *
+     * @param json A JSON representation of an {@link KeyedFrequencyCap} object as would be
+     *     generated by {@link #toJson()}.
+     * @return An {@link KeyedFrequencyCap} object generated from the given JSON.
+     * @hide
+     */
+    public static KeyedFrequencyCap fromJson(JSONObject json) throws JSONException {
+        return new Builder(
+                        json.getInt(AD_COUNTER_KEY_FIELD_NAME),
+                        json.getInt(MAX_COUNT_FIELD_NAME),
+                        Duration.ofSeconds(json.getLong(INTERVAL_FIELD_NAME)))
+                .build();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+        dest.writeInt(mAdCounterKey);
+        dest.writeInt(mMaxCount);
+        dest.writeLong(mInterval.getSeconds());
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Checks whether the {@link KeyedFrequencyCap} objects contain the same information. */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof KeyedFrequencyCap)) return false;
+        KeyedFrequencyCap that = (KeyedFrequencyCap) o;
+        return mMaxCount == that.mMaxCount
+                && mInterval.equals(that.mInterval)
+                && mAdCounterKey == that.mAdCounterKey;
+    }
+
+    /** Returns the hash of the {@link KeyedFrequencyCap} object's data. */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAdCounterKey, mMaxCount, mInterval);
+    }
+
+    @Override
+    public String toString() {
+        return "KeyedFrequencyCap{"
+                + "mAdCounterKey="
+                + mAdCounterKey
+                + ", mMaxCount="
+                + mMaxCount
+                + ", mInterval="
+                + mInterval
+                + '}';
+    }
+
+    /** Builder for creating {@link KeyedFrequencyCap} objects. */
+    public static final class Builder {
+        private int mAdCounterKey;
+        private int mMaxCount;
+        @NonNull private Duration mInterval;
+
+        public Builder(int adCounterKey, int maxCount, @NonNull Duration interval) {
+            Preconditions.checkArgument(
+                    maxCount > 0, MAX_COUNT_NOT_POSITIVE_ERROR_MESSAGE, maxCount);
+            Objects.requireNonNull(interval, INTERVAL_NULL_ERROR_MESSAGE);
+            Preconditions.checkArgument(
+                    interval.getSeconds() > 0, INTERVAL_NOT_POSITIVE_FORMAT, interval);
+            Preconditions.checkArgument(
+                    interval.getSeconds() <= MAX_INTERVAL.getSeconds(),
+                    MAX_INTERVAL_EXCEEDED_FORMAT,
+                    interval,
+                    MAX_INTERVAL);
+
+            mAdCounterKey = adCounterKey;
+            mMaxCount = maxCount;
+            mInterval = interval;
+        }
+
+        /**
+         * Sets the ad counter key the frequency cap applies to.
+         *
+         * <p>See {@link #getAdCounterKey()} for more information.
+         */
+        @NonNull
+        public Builder setAdCounterKey(int adCounterKey) {
+            mAdCounterKey = adCounterKey;
+            return this;
+        }
+
+        /**
+         * Sets the maximum count within the time interval for the frequency cap.
+         *
+         * <p>See {@link #getMaxCount()} for more information.
+         */
+        @NonNull
+        public Builder setMaxCount(int maxCount) {
+            Preconditions.checkArgument(
+                    maxCount > 0, MAX_COUNT_NOT_POSITIVE_ERROR_MESSAGE, maxCount);
+            mMaxCount = maxCount;
+            return this;
+        }
+
+        /**
+         * Sets the interval, as a {@link Duration} which will be truncated to the nearest second,
+         * over which the frequency cap is calculated.
+         *
+         * <p>See {@link #getInterval()} for more information.
+         */
+        @NonNull
+        public Builder setInterval(@NonNull Duration interval) {
+            Objects.requireNonNull(interval, INTERVAL_NULL_ERROR_MESSAGE);
+            Preconditions.checkArgument(
+                    interval.getSeconds() > 0, INTERVAL_NOT_POSITIVE_FORMAT, interval);
+            Preconditions.checkArgument(
+                    interval.getSeconds() <= MAX_INTERVAL.getSeconds(),
+                    MAX_INTERVAL_EXCEEDED_FORMAT,
+                    interval,
+                    MAX_INTERVAL);
+            mInterval = interval;
+            return this;
+        }
+
+        /** Builds and returns a {@link KeyedFrequencyCap} instance. */
+        @NonNull
+        public KeyedFrequencyCap build() {
+            return new KeyedFrequencyCap(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/common/OutcomeReceiverConverter.java b/android-35/android/adservices/common/OutcomeReceiverConverter.java
new file mode 100644
index 0000000..39048c8
--- /dev/null
+++ b/android-35/android/adservices/common/OutcomeReceiverConverter.java
@@ -0,0 +1,64 @@
+/*
+ * 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 android.adservices.common;
+
+import android.annotation.NonNull;
+import android.os.Build;
+import android.os.OutcomeReceiver;
+
+import androidx.annotation.RequiresApi;
+
+/**
+ * Utility class to convert between {@link OutcomeReceiver} and {@link AdServicesOutcomeReceiver}.
+ *
+ * @hide
+ */
+@RequiresApi(Build.VERSION_CODES.S)
+public final class OutcomeReceiverConverter {
+    private OutcomeReceiverConverter() {
+        // Prevent instantiation
+    }
+
+    /**
+     * Converts an instance of {@link OutcomeReceiver} to a custom {@link
+     * AdServicesOutcomeReceiver}.
+     *
+     * @param callback the instance of {@link OutcomeReceiver} to wrap
+     * @return an {@link AdServicesOutcomeReceiver} that wraps the original input
+     * @param <R> the type of Result that the receiver can process
+     * @param <E> the type of Exception that can be handled by the receiver
+     */
+    public static <R, E extends Throwable>
+            AdServicesOutcomeReceiver<R, E> toAdServicesOutcomeReceiver(
+                    OutcomeReceiver<R, E> callback) {
+        if (callback == null) {
+            return null;
+        }
+
+        return new AdServicesOutcomeReceiver<R, E>() {
+            @Override
+            public void onResult(R result) {
+                callback.onResult(result);
+            }
+
+            @Override
+            public void onError(@NonNull E error) {
+                callback.onError(error);
+            }
+        };
+    }
+}
diff --git a/android-35/android/adservices/common/SandboxedSdkContextUtils.java b/android-35/android/adservices/common/SandboxedSdkContextUtils.java
new file mode 100644
index 0000000..c138229
--- /dev/null
+++ b/android-35/android/adservices/common/SandboxedSdkContextUtils.java
@@ -0,0 +1,52 @@
+/*
+ * 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 android.adservices.common;
+
+import android.app.sdksandbox.SandboxedSdkContext;
+import android.content.Context;
+import android.os.Build;
+
+/**
+ * Class containing some utility functions used by other methods within AdServices.
+ *
+ * @hide
+ */
+public final class SandboxedSdkContextUtils {
+    private SandboxedSdkContextUtils() {
+        // Intended to be a utility class that should not be instantiated.
+    }
+
+    /**
+     * Checks if the context is an instance of SandboxedSdkContext.
+     *
+     * @param context the object to check and cast to {@link SandboxedSdkContext}
+     * @return the context object cast to {@link SandboxedSdkContext} if it is an instance of {@link
+     *     SandboxedSdkContext}, or {@code null} otherwise.
+     */
+    public static SandboxedSdkContext getAsSandboxedSdkContext(Context context) {
+        // TODO(b/266693417): Replace build version check with SdkLevel.isAtLeastT()
+        if (context == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+            return null; // SandboxedSdkContext is only available in T+
+        }
+
+        if (!(context instanceof SandboxedSdkContext)) {
+            return null;
+        }
+
+        return (SandboxedSdkContext) context;
+    }
+}
diff --git a/android-35/android/adservices/common/UpdateAdIdRequest.java b/android-35/android/adservices/common/UpdateAdIdRequest.java
new file mode 100644
index 0000000..3b9ee5a
--- /dev/null
+++ b/android-35/android/adservices/common/UpdateAdIdRequest.java
@@ -0,0 +1,152 @@
+/*
+ * 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 android.adservices.common;
+
+import android.adservices.adid.AdId;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.flags.Flags;
+
+import java.util.Objects;
+
+/**
+ * The request sent from the AdIdProvider to update the AdId in Adservices, when the device updates
+ * the AdId.
+ *
+ * @hide
+ */
+// TODO(b/300445889): Consider using codegen for Parcelable.
+@SystemApi
+@FlaggedApi(Flags.FLAG_AD_ID_CACHE_ENABLED)
+public final class UpdateAdIdRequest implements Parcelable {
+    private final String mAdId;
+    private final boolean mLimitAdTrackingEnabled;
+
+    private UpdateAdIdRequest(String adId, boolean isLimitAdTrackingEnabled) {
+        mAdId = Objects.requireNonNull(adId);
+        mLimitAdTrackingEnabled = isLimitAdTrackingEnabled;
+    }
+
+    private UpdateAdIdRequest(Parcel in) {
+        this(in.readString(), in.readBoolean());
+    }
+
+    @NonNull
+    public static final Creator<UpdateAdIdRequest> CREATOR =
+            new Parcelable.Creator<>() {
+                @Override
+                public UpdateAdIdRequest createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new UpdateAdIdRequest(in);
+                }
+
+                @Override
+                public UpdateAdIdRequest[] newArray(int size) {
+                    return new UpdateAdIdRequest[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        Objects.requireNonNull(out);
+
+        out.writeString(mAdId);
+        out.writeBoolean(mLimitAdTrackingEnabled);
+    }
+
+    /** Returns the advertising ID associated with this result. */
+    @NonNull
+    public String getAdId() {
+        return mAdId;
+    }
+
+    /**
+     * Returns the Limited Ad Tracking field associated with this result.
+     *
+     * <p>When Limited Ad Tracking is enabled, it implies the user opts out the usage of {@link
+     * AdId}. {@link AdId#ZERO_OUT} will be assigned to the device.
+     */
+    public boolean isLimitAdTrackingEnabled() {
+        return mLimitAdTrackingEnabled;
+    }
+
+    // TODO(b/302682607): Investigate encoding AdId in logcat for related AdId classes.
+    @Override
+    public String toString() {
+        return "UpdateAdIdRequest{"
+                + "mAdId="
+                + mAdId
+                + ", mLimitAdTrackingEnabled="
+                + mLimitAdTrackingEnabled
+                + '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof UpdateAdIdRequest)) {
+            return false;
+        }
+
+        UpdateAdIdRequest that = (UpdateAdIdRequest) o;
+
+        return Objects.equals(mAdId, that.mAdId)
+                && (mLimitAdTrackingEnabled == that.mLimitAdTrackingEnabled);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAdId, mLimitAdTrackingEnabled);
+    }
+
+    /** Builder for {@link UpdateAdIdRequest} objects. */
+    public static final class Builder {
+        private final String mAdId;
+        private boolean mLimitAdTrackingEnabled;
+
+        public Builder(@NonNull String adId) {
+            mAdId = Objects.requireNonNull(adId);
+        }
+
+        /** Sets the Limited AdTracking enabled field. */
+        @NonNull
+        public UpdateAdIdRequest.Builder setLimitAdTrackingEnabled(
+                boolean isLimitAdTrackingEnabled) {
+            mLimitAdTrackingEnabled = isLimitAdTrackingEnabled;
+            return this;
+        }
+
+        /** Builds a {@link UpdateAdIdRequest} instance. */
+        @NonNull
+        public UpdateAdIdRequest build() {
+            return new UpdateAdIdRequest(mAdId, mLimitAdTrackingEnabled);
+        }
+    }
+}
diff --git a/android-35/android/adservices/customaudience/AddCustomAudienceOverrideRequest.java b/android-35/android/adservices/customaudience/AddCustomAudienceOverrideRequest.java
new file mode 100644
index 0000000..8e9bfa0
--- /dev/null
+++ b/android-35/android/adservices/customaudience/AddCustomAudienceOverrideRequest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.customaudience;
+
+import android.adservices.common.AdSelectionSignals;
+import android.adservices.common.AdTechIdentifier;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.OutcomeReceiver;
+
+import com.android.adservices.flags.Flags;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * This POJO represents the {@link
+ * TestCustomAudienceManager#overrideCustomAudienceRemoteInfo(AddCustomAudienceOverrideRequest,
+ * Executor, OutcomeReceiver)} request.
+ *
+ * <p>It contains fields {@code buyer} and {@code name} which will serve as the identifier for the
+ * override fields, {@code biddingLogicJs} and {@code trustedBiddingSignals}, which are used during
+ * ad selection instead of querying external servers.
+ */
+public class AddCustomAudienceOverrideRequest {
+    @NonNull private final AdTechIdentifier mBuyer;
+    @NonNull private final String mName;
+    @NonNull private final String mBiddingLogicJs;
+    private final long mBiddingLogicJsVersion;
+    @NonNull private final AdSelectionSignals mTrustedBiddingSignals;
+
+    public AddCustomAudienceOverrideRequest(
+            @NonNull AdTechIdentifier buyer,
+            @NonNull String name,
+            @NonNull String biddingLogicJs,
+            @NonNull AdSelectionSignals trustedBiddingSignals) {
+        this(buyer, name, biddingLogicJs, 0L, trustedBiddingSignals);
+    }
+
+    private AddCustomAudienceOverrideRequest(
+            @NonNull AdTechIdentifier buyer,
+            @NonNull String name,
+            @NonNull String biddingLogicJs,
+            long biddingLogicJsVersion,
+            @NonNull AdSelectionSignals trustedBiddingSignals) {
+        mBuyer = buyer;
+        mName = name;
+        mBiddingLogicJs = biddingLogicJs;
+        mBiddingLogicJsVersion = biddingLogicJsVersion;
+        mTrustedBiddingSignals = trustedBiddingSignals;
+    }
+
+    /** @return an {@link AdTechIdentifier} representing the buyer */
+    @NonNull
+    public AdTechIdentifier getBuyer() {
+        return mBuyer;
+    }
+
+    /** @return name of the custom audience being overridden */
+    @NonNull
+    public String getName() {
+        return mName;
+    }
+
+    /** @return the override JavaScript result that should be served during ad selection */
+    @NonNull
+    public String getBiddingLogicJs() {
+        return mBiddingLogicJs;
+    }
+
+    /**
+     * Returns the value to return as version for JavaScript bidding logic.
+     *
+     * <p>Default to be {@code 0L}, which will fall back to use default version(V1 or V2).
+     */
+    @FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
+    public long getBiddingLogicJsVersion() {
+        return mBiddingLogicJsVersion;
+    }
+
+    /** @return the override trusted bidding signals that should be served during ad selection */
+    @NonNull
+    public AdSelectionSignals getTrustedBiddingSignals() {
+        return mTrustedBiddingSignals;
+    }
+
+    /** Builder for {@link AddCustomAudienceOverrideRequest} objects. */
+    public static final class Builder {
+        @Nullable private AdTechIdentifier mBuyer;
+        @Nullable private String mName;
+        @Nullable private String mBiddingLogicJs;
+        private long mBiddingLogicJsVersion;
+        @Nullable private AdSelectionSignals mTrustedBiddingSignals;
+
+        public Builder() {}
+
+        /** Sets the buyer {@link AdTechIdentifier} for the custom audience. */
+        @NonNull
+        public AddCustomAudienceOverrideRequest.Builder setBuyer(@NonNull AdTechIdentifier buyer) {
+            Objects.requireNonNull(buyer);
+
+            this.mBuyer = buyer;
+            return this;
+        }
+
+        /** Sets the name for the custom audience to be overridden. */
+        @NonNull
+        public AddCustomAudienceOverrideRequest.Builder setName(@NonNull String name) {
+            Objects.requireNonNull(name);
+
+            this.mName = name;
+            return this;
+        }
+
+        /** Sets the trusted bidding signals to be served during ad selection. */
+        @NonNull
+        public AddCustomAudienceOverrideRequest.Builder setTrustedBiddingSignals(
+                @NonNull AdSelectionSignals trustedBiddingSignals) {
+            Objects.requireNonNull(trustedBiddingSignals);
+
+            this.mTrustedBiddingSignals = trustedBiddingSignals;
+            return this;
+        }
+
+        /** Sets the bidding logic JavaScript that should be served during ad selection. */
+        @NonNull
+        public AddCustomAudienceOverrideRequest.Builder setBiddingLogicJs(
+                @NonNull String biddingLogicJs) {
+            Objects.requireNonNull(biddingLogicJs);
+
+            this.mBiddingLogicJs = biddingLogicJs;
+            return this;
+        }
+
+        /**
+         * Sets the bidding logic JavaScript version.
+         *
+         * <p>Default to be {@code 0L}, which will fall back to use default version(V1 or V2).
+         */
+        @FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
+        @NonNull
+        public AddCustomAudienceOverrideRequest.Builder setBiddingLogicJsVersion(
+                long biddingLogicJsVersion) {
+            this.mBiddingLogicJsVersion = biddingLogicJsVersion;
+            return this;
+        }
+
+        /** Builds a {@link AddCustomAudienceOverrideRequest} instance. */
+        @NonNull
+        public AddCustomAudienceOverrideRequest build() {
+            Objects.requireNonNull(mBuyer);
+            Objects.requireNonNull(mName);
+            Objects.requireNonNull(mBiddingLogicJs);
+            Objects.requireNonNull(mTrustedBiddingSignals);
+
+            return new AddCustomAudienceOverrideRequest(
+                    mBuyer, mName, mBiddingLogicJs, mBiddingLogicJsVersion, mTrustedBiddingSignals);
+        }
+    }
+}
diff --git a/android-35/android/adservices/customaudience/CustomAudience.java b/android-35/android/adservices/customaudience/CustomAudience.java
new file mode 100644
index 0000000..baf644e
--- /dev/null
+++ b/android-35/android/adservices/customaudience/CustomAudience.java
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.customaudience;
+
+import android.adservices.adselection.GetAdSelectionDataRequest;
+import android.adservices.common.AdData;
+import android.adservices.common.AdSelectionSignals;
+import android.adservices.common.AdTechIdentifier;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.OutcomeReceiver;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.AdServicesParcelableUtil;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Represents the information necessary for a custom audience to participate in ad selection.
+ *
+ * <p>A custom audience is an abstract grouping of users with similar demonstrated interests. This
+ * class is a collection of some data stored on a device that is necessary to serve advertisements
+ * targeting a single custom audience.
+ */
+public final class CustomAudience implements Parcelable {
+    /** @hide */
+    public static final int FLAG_AUCTION_SERVER_REQUEST_DEFAULT = 0;
+
+    /**
+     * This auction server request flag indicates to the service that ads for this {@link
+     * CustomAudience} can be omitted in the server auction payload.
+     */
+    @FlaggedApi(
+            "com.android.adservices.flags.fledge_custom_audience_auction_server_request_flags_enabled")
+    public static final int FLAG_AUCTION_SERVER_REQUEST_OMIT_ADS = 1 << 0;
+
+    @NonNull private final AdTechIdentifier mBuyer;
+    @NonNull private final String mName;
+    @Nullable private final Instant mActivationTime;
+    @Nullable private final Instant mExpirationTime;
+    @NonNull private final Uri mDailyUpdateUri;
+    @Nullable private final AdSelectionSignals mUserBiddingSignals;
+    @Nullable private final TrustedBiddingData mTrustedBiddingData;
+    @NonNull private final Uri mBiddingLogicUri;
+    @NonNull private final List<AdData> mAds;
+    @AuctionServerRequestFlag private final int mAuctionServerRequestFlags;
+
+    /** @hide */
+    @IntDef(
+            flag = true,
+            prefix = {"FLAG_AUCTION_SERVER_REQUEST"},
+            value = {FLAG_AUCTION_SERVER_REQUEST_DEFAULT, FLAG_AUCTION_SERVER_REQUEST_OMIT_ADS})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AuctionServerRequestFlag {}
+
+    @NonNull
+    public static final Creator<CustomAudience> CREATOR = new Creator<CustomAudience>() {
+        @Override
+        public CustomAudience createFromParcel(@NonNull Parcel in) {
+            Objects.requireNonNull(in);
+
+            return new CustomAudience(in);
+        }
+
+        @Override
+        public CustomAudience[] newArray(int size) {
+            return new CustomAudience[size];
+        }
+    };
+
+    private CustomAudience(@NonNull CustomAudience.Builder builder) {
+        Objects.requireNonNull(builder);
+
+        mBuyer = builder.mBuyer;
+        mName = builder.mName;
+        mActivationTime = builder.mActivationTime;
+        mExpirationTime = builder.mExpirationTime;
+        mDailyUpdateUri = builder.mDailyUpdateUri;
+        mUserBiddingSignals = builder.mUserBiddingSignals;
+        mTrustedBiddingData = builder.mTrustedBiddingData;
+        mBiddingLogicUri = builder.mBiddingLogicUri;
+        mAds = builder.mAds;
+        mAuctionServerRequestFlags = builder.mAuctionServerRequestFlags;
+    }
+
+    private CustomAudience(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        mBuyer = AdTechIdentifier.CREATOR.createFromParcel(in);
+        mName = in.readString();
+        mActivationTime =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, (sourceParcel) -> Instant.ofEpochMilli(sourceParcel.readLong()));
+        mExpirationTime =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, (sourceParcel) -> Instant.ofEpochMilli(sourceParcel.readLong()));
+        mDailyUpdateUri = Uri.CREATOR.createFromParcel(in);
+        mUserBiddingSignals =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, AdSelectionSignals.CREATOR::createFromParcel);
+        mTrustedBiddingData =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, TrustedBiddingData.CREATOR::createFromParcel);
+        mBiddingLogicUri = Uri.CREATOR.createFromParcel(in);
+        mAds = in.createTypedArrayList(AdData.CREATOR);
+        mAuctionServerRequestFlags = in.readInt();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        mBuyer.writeToParcel(dest, flags);
+        dest.writeString(mName);
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mActivationTime,
+                (targetParcel, sourceInstant) ->
+                        targetParcel.writeLong(sourceInstant.toEpochMilli()));
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mExpirationTime,
+                (targetParcel, sourceInstant) ->
+                        targetParcel.writeLong(sourceInstant.toEpochMilli()));
+        mDailyUpdateUri.writeToParcel(dest, flags);
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mUserBiddingSignals,
+                (targetParcel, sourceSignals) -> sourceSignals.writeToParcel(targetParcel, flags));
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mTrustedBiddingData,
+                (targetParcel, sourceData) -> sourceData.writeToParcel(targetParcel, flags));
+        mBiddingLogicUri.writeToParcel(dest, flags);
+        dest.writeTypedList(mAds);
+        dest.writeInt(mAuctionServerRequestFlags);
+    }
+
+    @Override
+    public String toString() {
+        return "CustomAudience{"
+                + "mBuyer="
+                + mBuyer
+                + ", mName='"
+                + mName
+                + ", mActivationTime="
+                + mActivationTime
+                + ", mExpirationTime="
+                + mExpirationTime
+                + ", mDailyUpdateUri="
+                + mDailyUpdateUri
+                + ", mUserBiddingSignals="
+                + mUserBiddingSignals
+                + ", mTrustedBiddingData="
+                + mTrustedBiddingData
+                + ", mBiddingLogicUri="
+                + mBiddingLogicUri
+                + ", mAds="
+                + mAds
+                + ", mAuctionServerRequestFlags="
+                + mAuctionServerRequestFlags
+                + '}';
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * A buyer is identified by a domain in the form "buyerexample.com".
+     *
+     * @return an {@link AdTechIdentifier} containing the custom audience's buyer's domain
+     */
+    @NonNull
+    public AdTechIdentifier getBuyer() {
+        return mBuyer;
+    }
+
+    /**
+     * The custom audience's name is an arbitrary string provided by the owner and buyer on creation
+     * of the {@link CustomAudience} object.
+     *
+     * <p>The overall size of the CA is limited and the size of this field is considered using
+     * {@link String#getBytes()} in {@code UTF-8} encoding.
+     *
+     * @return the String name of the custom audience
+     */
+    @NonNull
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * On creation of the {@link CustomAudience} object, an optional activation time may be set in
+     * the future, in order to serve a delayed activation. If the field is not set, the {@link
+     * CustomAudience} will be activated at the time of joining.
+     *
+     * <p>For example, a custom audience for lapsed users may not activate until a threshold of
+     * inactivity is reached, at which point the custom audience's ads will participate in the ad
+     * selection process, potentially redirecting lapsed users to the original owner application.
+     *
+     * <p>The maximum delay in activation is 60 days from initial creation.
+     *
+     * <p>If specified, the activation time must be an earlier instant than the expiration time.
+     *
+     * @return the timestamp {@link Instant}, truncated to milliseconds, after which the custom
+     *     audience is active
+     */
+    @Nullable
+    public Instant getActivationTime() {
+        return mActivationTime;
+    }
+
+    /**
+     * Once the expiration time has passed, a custom audience is no longer eligible for daily
+     * ad/bidding data updates or participation in the ad selection process. The custom audience
+     * will then be deleted from memory by the next daily update.
+     *
+     * <p>If no expiration time is provided on creation of the {@link CustomAudience}, expiry will
+     * default to 60 days from activation.
+     *
+     * <p>The maximum expiry is 60 days from initial activation.
+     *
+     * @return the timestamp {@link Instant}, truncated to milliseconds, after which the custom
+     *     audience should be removed
+     */
+    @Nullable
+    public Instant getExpirationTime() {
+        return mExpirationTime;
+    }
+
+    /**
+     * This URI points to a buyer-operated server that hosts updated bidding data and ads metadata
+     * to be used in the on-device ad selection process. The URI must use HTTPS.
+     *
+     * @return the custom audience's daily update URI
+     */
+    @NonNull
+    public Uri getDailyUpdateUri() {
+        return mDailyUpdateUri;
+    }
+
+    /**
+     * User bidding signals are optionally provided by buyers to be consumed by buyer-provided
+     * JavaScript during ad selection in an isolated execution environment.
+     *
+     * <p>If the user bidding signals are not a valid JSON object that can be consumed by the
+     * buyer's JS, the custom audience will not be eligible for ad selection.
+     *
+     * <p>If not specified, the {@link CustomAudience} will not participate in ad selection until
+     * user bidding signals are provided via the daily update for the custom audience.
+     *
+     * @return an {@link AdSelectionSignals} object representing the user bidding signals for the
+     *     custom audience
+     */
+    @Nullable
+    public AdSelectionSignals getUserBiddingSignals() {
+        return mUserBiddingSignals;
+    }
+
+    /**
+     * Trusted bidding data consists of a URI pointing to a trusted server for buyers' bidding data
+     * and a list of keys to query the server with. Note that the keys are arbitrary identifiers
+     * that will only be used to query the trusted server for a buyer's bidding logic during ad
+     * selection.
+     *
+     * <p>If not specified, the {@link CustomAudience} will not participate in ad selection until
+     * trusted bidding data are provided via the daily update for the custom audience.
+     *
+     * @return a {@link TrustedBiddingData} object containing the custom audience's trusted bidding
+     *     data
+     */
+    @Nullable
+    public TrustedBiddingData getTrustedBiddingData() {
+        return mTrustedBiddingData;
+    }
+
+    /**
+     * Returns the target URI used to fetch bidding logic when a custom audience participates in the
+     * ad selection process. The URI must use HTTPS.
+     *
+     * @return the URI for fetching buyer bidding logic
+     */
+    @NonNull
+    public Uri getBiddingLogicUri() {
+        return mBiddingLogicUri;
+    }
+
+    /**
+     * This list of {@link AdData} objects is a full and complete list of the ads that will be
+     * served by this {@link CustomAudience} during the ad selection process.
+     *
+     * <p>If not specified, or if an empty list is provided, the {@link CustomAudience} will not
+     * participate in ad selection until a valid list of ads are provided via the daily update for
+     * the custom audience.
+     *
+     * <p>The combined ads size of the CA is limited and the sizes of each ad's string fields are
+     * considered using {@link String#getBytes()} in {@code UTF-8} encoding.
+     *
+     * @return a {@link List} of {@link AdData} objects representing ads currently served by the
+     *     custom audience
+     */
+    @NonNull
+    public List<AdData> getAds() {
+        return mAds;
+    }
+
+    /**
+     * Returns the bitfield of auction server request flags. These are flags that influence the
+     * creation of the payload generated by the {@link
+     * android.adservices.adselection.AdSelectionManager#getAdSelectionData(GetAdSelectionDataRequest,
+     * Executor, OutcomeReceiver)} API.
+     *
+     * <p>To create this bitfield, place an {@code |} bitwise operator between each {@link
+     * AuctionServerRequestFlag} to be enabled.
+     */
+    @FlaggedApi(
+            "com.android.adservices.flags.fledge_custom_audience_auction_server_request_flags_enabled")
+    @AuctionServerRequestFlag
+    public int getAuctionServerRequestFlags() {
+        return mAuctionServerRequestFlags;
+    }
+
+    /**
+     * Checks whether two {@link CustomAudience} objects contain the same information.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CustomAudience)) return false;
+        CustomAudience that = (CustomAudience) o;
+        return mBuyer.equals(that.mBuyer)
+                && mName.equals(that.mName)
+                && Objects.equals(mActivationTime, that.mActivationTime)
+                && Objects.equals(mExpirationTime, that.mExpirationTime)
+                && mDailyUpdateUri.equals(that.mDailyUpdateUri)
+                && Objects.equals(mUserBiddingSignals, that.mUserBiddingSignals)
+                && Objects.equals(mTrustedBiddingData, that.mTrustedBiddingData)
+                && mBiddingLogicUri.equals(that.mBiddingLogicUri)
+                && mAds.equals(that.mAds)
+                && mAuctionServerRequestFlags == that.mAuctionServerRequestFlags;
+    }
+
+    /**
+     * Returns the hash of the {@link CustomAudience} object's data.
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mBuyer,
+                mName,
+                mActivationTime,
+                mExpirationTime,
+                mDailyUpdateUri,
+                mUserBiddingSignals,
+                mTrustedBiddingData,
+                mBiddingLogicUri,
+                mAds,
+                mAuctionServerRequestFlags);
+    }
+
+    /** Builder for {@link CustomAudience} objects. */
+    public static final class Builder {
+        @Nullable private AdTechIdentifier mBuyer;
+        @Nullable private String mName;
+        @Nullable private Instant mActivationTime;
+        @Nullable private Instant mExpirationTime;
+        @Nullable private Uri mDailyUpdateUri;
+        @Nullable private AdSelectionSignals mUserBiddingSignals;
+        @Nullable private TrustedBiddingData mTrustedBiddingData;
+        @Nullable private Uri mBiddingLogicUri;
+        @Nullable private List<AdData> mAds;
+        @AuctionServerRequestFlag private int mAuctionServerRequestFlags;
+
+        // TODO(b/232883403): We may need to add @NonNUll members as args.
+        public Builder() {
+        }
+
+        /**
+         * Sets the buyer {@link AdTechIdentifier}.
+         *
+         * <p>See {@link #getBuyer()} for more information.
+         */
+        @NonNull
+        public CustomAudience.Builder setBuyer(@NonNull AdTechIdentifier buyer) {
+            Objects.requireNonNull(buyer);
+            mBuyer = buyer;
+            return this;
+        }
+
+        /**
+         * Sets the {@link CustomAudience} object's name.
+         * <p>
+         * See {@link #getName()} for more information.
+         */
+        @NonNull
+        public CustomAudience.Builder setName(@NonNull String name) {
+            Objects.requireNonNull(name);
+            mName = name;
+            return this;
+        }
+
+        /**
+         * Sets the time, truncated to milliseconds, after which the {@link CustomAudience} will
+         * serve ads.
+         *
+         * <p>Set to {@code null} in order for this {@link CustomAudience} to be immediately active
+         * and participate in ad selection.
+         *
+         * <p>See {@link #getActivationTime()} for more information.
+         */
+        @NonNull
+        public CustomAudience.Builder setActivationTime(@Nullable Instant activationTime) {
+            mActivationTime = activationTime;
+            return this;
+        }
+
+        /**
+         * Sets the time, truncated to milliseconds, after which the {@link CustomAudience} should
+         * be removed.
+         * <p>
+         * See {@link #getExpirationTime()} for more information.
+         */
+        @NonNull
+        public CustomAudience.Builder setExpirationTime(@Nullable Instant expirationTime) {
+            mExpirationTime = expirationTime;
+            return this;
+        }
+
+        /**
+         * Sets the daily update URI. The URI must use HTTPS.
+         *
+         * <p>See {@link #getDailyUpdateUri()} for more information.
+         */
+        @NonNull
+        public CustomAudience.Builder setDailyUpdateUri(@NonNull Uri dailyUpdateUri) {
+            Objects.requireNonNull(dailyUpdateUri);
+            mDailyUpdateUri = dailyUpdateUri;
+            return this;
+        }
+
+        /**
+         * Sets the user bidding signals used in the ad selection process.
+         *
+         * <p>See {@link #getUserBiddingSignals()} for more information.
+         */
+        @NonNull
+        public CustomAudience.Builder setUserBiddingSignals(
+                @Nullable AdSelectionSignals userBiddingSignals) {
+            mUserBiddingSignals = userBiddingSignals;
+            return this;
+        }
+
+        /**
+         * Sets the trusted bidding data to be queried and used in the ad selection process.
+         * <p>
+         * See {@link #getTrustedBiddingData()} for more information.
+         */
+        @NonNull
+        public CustomAudience.Builder setTrustedBiddingData(
+                @Nullable TrustedBiddingData trustedBiddingData) {
+            mTrustedBiddingData = trustedBiddingData;
+            return this;
+        }
+
+        /**
+         * Sets the URI to fetch bidding logic from for use in the ad selection process. The URI
+         * must use HTTPS.
+         *
+         * <p>See {@link #getBiddingLogicUri()} for more information.
+         */
+        @NonNull
+        public CustomAudience.Builder setBiddingLogicUri(@NonNull Uri biddingLogicUri) {
+            Objects.requireNonNull(biddingLogicUri);
+            mBiddingLogicUri = biddingLogicUri;
+            return this;
+        }
+
+        /**
+         * Sets the initial remarketing ads served by the custom audience. Will be assigned with an
+         * empty list if not provided.
+         *
+         * <p>See {@link #getAds()} for more information.
+         */
+        @NonNull
+        public CustomAudience.Builder setAds(@Nullable List<AdData> ads) {
+            mAds = ads;
+            return this;
+        }
+
+        /**
+         * Sets the bitfield of auction server request flags.
+         *
+         * <p>See {@link #getAuctionServerRequestFlags()} for more information.
+         */
+        @FlaggedApi(
+                "com.android.adservices.flags.fledge_custom_audience_auction_server_request_flags_enabled")
+        @NonNull
+        public CustomAudience.Builder setAuctionServerRequestFlags(
+                @AuctionServerRequestFlag int auctionServerRequestFlags) {
+            mAuctionServerRequestFlags = auctionServerRequestFlags;
+            return this;
+        }
+
+        /**
+         * Builds an instance of a {@link CustomAudience}.
+         *
+         * @throws NullPointerException     if any non-null parameter is null
+         * @throws IllegalArgumentException if the expiration time occurs before activation time
+         * @throws IllegalArgumentException if the expiration time is set before the current time
+         */
+        @NonNull
+        public CustomAudience build() {
+            Objects.requireNonNull(mBuyer, "The buyer has not been provided");
+            Objects.requireNonNull(mName, "The name has not been provided");
+            Objects.requireNonNull(mDailyUpdateUri, "The daily update URI has not been provided");
+            Objects.requireNonNull(mBiddingLogicUri, "The bidding logic URI has not been provided");
+
+            // To pass the API lint, we should not allow null Collection.
+            if (mAds == null) {
+                mAds = List.of();
+            }
+
+            return new CustomAudience(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/customaudience/CustomAudienceManager.java b/android-35/android/adservices/customaudience/CustomAudienceManager.java
new file mode 100644
index 0000000..54e959e
--- /dev/null
+++ b/android-35/android/adservices/customaudience/CustomAudienceManager.java
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.customaudience;
+
+import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE;
+
+import static com.android.adservices.flags.Flags.FLAG_FLEDGE_SCHEDULE_CUSTOM_AUDIENCE_UPDATE_ENABLED;
+
+import android.adservices.common.AdServicesOutcomeReceiver;
+import android.adservices.common.AdServicesStatusUtils;
+import android.adservices.common.AdTechIdentifier;
+import android.adservices.common.FledgeErrorResponse;
+import android.adservices.common.SandboxedSdkContextUtils;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.app.sdksandbox.SandboxedSdkContext;
+import android.content.Context;
+import android.os.Build;
+import android.os.LimitExceededException;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.adservices.AdServicesCommon;
+import com.android.adservices.LoggerFactory;
+import com.android.adservices.ServiceBinder;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/** CustomAudienceManager provides APIs for app and ad-SDKs to join / leave custom audiences. */
+@RequiresApi(Build.VERSION_CODES.S)
+public class CustomAudienceManager {
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger();
+    /**
+     * Constant that represents the service name for {@link CustomAudienceManager} to be used in
+     * {@link android.adservices.AdServicesFrameworkInitializer#registerServiceWrappers}
+     *
+     * @hide
+     */
+    public static final String CUSTOM_AUDIENCE_SERVICE = "custom_audience_service";
+
+    @NonNull private Context mContext;
+    @NonNull private ServiceBinder<ICustomAudienceService> mServiceBinder;
+
+    /**
+     * Factory method for creating an instance of CustomAudienceManager.
+     *
+     * @param context The {@link Context} to use
+     * @return A {@link CustomAudienceManager} instance
+     */
+    @NonNull
+    public static CustomAudienceManager get(@NonNull Context context) {
+        // On T+, context.getSystemService() does more than just call constructor.
+        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+                ? context.getSystemService(CustomAudienceManager.class)
+                : new CustomAudienceManager(context);
+    }
+
+    /**
+     * Create a service binder CustomAudienceManager
+     *
+     * @hide
+     */
+    public CustomAudienceManager(@NonNull Context context) {
+        Objects.requireNonNull(context);
+
+        // In case the CustomAudienceManager is initiated from inside a sdk_sandbox process the
+        // fields will be immediately rewritten by the initialize method below.
+        initialize(context);
+    }
+
+    /**
+     * Initializes {@link CustomAudienceManager} with the given {@code context}.
+     *
+     * <p>This method is called by the {@link SandboxedSdkContext} to propagate the correct context.
+     * For more information check the javadoc on the {@link
+     * android.app.sdksandbox.SdkSandboxSystemServiceRegistry}.
+     *
+     * @hide
+     * @see android.app.sdksandbox.SdkSandboxSystemServiceRegistry
+     */
+    public CustomAudienceManager initialize(@NonNull Context context) {
+        Objects.requireNonNull(context);
+
+        mContext = context;
+        mServiceBinder =
+                ServiceBinder.getServiceBinder(
+                        context,
+                        AdServicesCommon.ACTION_CUSTOM_AUDIENCE_SERVICE,
+                        ICustomAudienceService.Stub::asInterface);
+        return this;
+    }
+
+    /** Create a service with test-enabling APIs */
+    @NonNull
+    public TestCustomAudienceManager getTestCustomAudienceManager() {
+        return new TestCustomAudienceManager(this, getCallerPackageName());
+    }
+
+    @NonNull
+    ICustomAudienceService getService() {
+        ICustomAudienceService service = mServiceBinder.getService();
+        if (service == null) {
+            throw new IllegalStateException("custom audience service is not available.");
+        }
+        return service;
+    }
+
+    /**
+     * Adds the user to the given {@link CustomAudience}.
+     *
+     * <p>An attempt to register the user for a custom audience with the same combination of {@code
+     * ownerPackageName}, {@code buyer}, and {@code name} will cause the existing custom audience's
+     * information to be overwritten, including the list of ads data.
+     *
+     * <p>Note that the ads list can be completely overwritten by the daily background fetch job.
+     *
+     * <p>This call fails with an {@link SecurityException} if
+     *
+     * <ol>
+     *   <li>the {@code ownerPackageName} is not calling app's package name and/or
+     *   <li>the buyer is not authorized to use the API.
+     * </ol>
+     *
+     * <p>This call fails with an {@link IllegalArgumentException} if
+     *
+     * <ol>
+     *   <li>the storage limit has been exceeded by the calling application and/or
+     *   <li>any URI parameters in the {@link CustomAudience} given are not authenticated with the
+     *       {@link CustomAudience} buyer.
+     * </ol>
+     *
+     * <p>This call fails with {@link LimitExceededException} if the calling package exceeds the
+     * allowed rate limits and is throttled.
+     *
+     * <p>This call fails with an {@link IllegalStateException} if an internal service error is
+     * encountered.
+     */
+    @RequiresPermission(ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
+    public void joinCustomAudience(
+            @NonNull JoinCustomAudienceRequest joinCustomAudienceRequest,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(joinCustomAudienceRequest);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+
+        final CustomAudience customAudience = joinCustomAudienceRequest.getCustomAudience();
+
+        try {
+            final ICustomAudienceService service = getService();
+
+            service.joinCustomAudience(
+                    customAudience,
+                    getCallerPackageName(),
+                    new ICustomAudienceCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            receiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Internal Error!", e));
+        }
+    }
+
+    /**
+     * Adds the user to the {@link CustomAudience} fetched from a {@code fetchUri}.
+     *
+     * <p>An attempt to register the user for a custom audience with the same combination of {@code
+     * ownerPackageName}, {@code buyer}, and {@code name} will cause the existing custom audience's
+     * information to be overwritten, including the list of ads data.
+     *
+     * <p>Note that the ads list can be completely overwritten by the daily background fetch job.
+     *
+     * <p>This call fails with an {@link SecurityException} if
+     *
+     * <ol>
+     *   <li>the {@code ownerPackageName} is not calling app's package name and/or
+     *   <li>the buyer is not authorized to use the API.
+     * </ol>
+     *
+     * <p>This call fails with an {@link IllegalArgumentException} if
+     *
+     * <ol>
+     *   <li>the storage limit has been exceeded by the calling application and/or
+     *   <li>any URI parameters in the {@link CustomAudience} given are not authenticated with the
+     *       {@link CustomAudience} buyer.
+     * </ol>
+     *
+     * <p>This call fails with {@link LimitExceededException} if the calling package exceeds the
+     * allowed rate limits and is throttled.
+     *
+     * <p>This call fails with an {@link IllegalStateException} if an internal service error is
+     * encountered.
+     */
+    @RequiresPermission(ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
+    public void fetchAndJoinCustomAudience(
+            @NonNull FetchAndJoinCustomAudienceRequest fetchAndJoinCustomAudienceRequest,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(fetchAndJoinCustomAudienceRequest);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+
+        try {
+            final ICustomAudienceService service = getService();
+
+            service.fetchAndJoinCustomAudience(
+                    new FetchAndJoinCustomAudienceInput.Builder(
+                                    fetchAndJoinCustomAudienceRequest.getFetchUri(),
+                                    getCallerPackageName())
+                            .setName(fetchAndJoinCustomAudienceRequest.getName())
+                            .setActivationTime(
+                                    fetchAndJoinCustomAudienceRequest.getActivationTime())
+                            .setExpirationTime(
+                                    fetchAndJoinCustomAudienceRequest.getExpirationTime())
+                            .setUserBiddingSignals(
+                                    fetchAndJoinCustomAudienceRequest.getUserBiddingSignals())
+                            .build(),
+                    new FetchAndJoinCustomAudienceCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            receiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Internal Error!", e));
+        }
+    }
+
+    /**
+     * Attempts to remove a user from a custom audience by deleting any existing {@link
+     * CustomAudience} data, identified by {@code ownerPackageName}, {@code buyer}, and {@code
+     * name}.
+     *
+     * <p>This call fails with an {@link SecurityException} if
+     *
+     * <ol>
+     *   <li>the {@code ownerPackageName} is not calling app's package name; and/or
+     *   <li>the buyer is not authorized to use the API.
+     * </ol>
+     *
+     * <p>This call fails with {@link LimitExceededException} if the calling package exceeds the
+     * allowed rate limits and is throttled.
+     *
+     * <p>This call does not inform the caller whether the custom audience specified existed in
+     * on-device storage. In other words, it will fail silently when a buyer attempts to leave a
+     * custom audience that was not joined.
+     */
+    @RequiresPermission(ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
+    public void leaveCustomAudience(
+            @NonNull LeaveCustomAudienceRequest leaveCustomAudienceRequest,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(leaveCustomAudienceRequest);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+
+        final AdTechIdentifier buyer = leaveCustomAudienceRequest.getBuyer();
+        final String name = leaveCustomAudienceRequest.getName();
+
+        try {
+            final ICustomAudienceService service = getService();
+
+            service.leaveCustomAudience(
+                    getCallerPackageName(),
+                    buyer,
+                    name,
+                    new ICustomAudienceCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            receiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Internal Error!", e));
+        }
+    }
+
+    /**
+     * Allows the API caller to schedule a deferred Custom Audience update. For each update the user
+     * will be able to join or leave a set of CustomAudiences.
+     *
+     * <p>This API only guarantees minimum delay to make the update, and does not guarantee a
+     * maximum deadline within which the update request would be made. Scheduled updates could be
+     * batched and queued together to preserve system resources, thus exact delay time is not
+     * guaranteed.
+     *
+     * <p>In order to conserve system resources the API will make and update request only if the
+     * following constraints are satisfied
+     *
+     * <ol>
+     *   <li>The device is using an un-metered internet connection
+     *   <li>The device battery is not low
+     *   <li>The device storage is not low
+     * </ol>
+     *
+     * <p>When the deferred update is triggered the API makes a POST request to the provided
+     * updateUri with the request body containing a JSON of Partial Custom Audience list.
+     *
+     * <p>An example of request body containing list of Partial Custom Audiences would look like:
+     *
+     * <pre>{@code
+     * {
+     *     "partial_custom_audience_data": [
+     *         {
+     *             "name": "running_shoes",
+     *             "activation_time": 1644375856883,
+     *             "expiration_time": 1644375908397
+     *         },
+     *         {
+     *             "name": "casual_shirt",
+     *             "user_bidding_signals": {
+     *                 "signal1": "value1"
+     *             }
+     *         }
+     *     ]
+     * }
+     * }</pre>
+     *
+     * <p>In response the API expects a JSON in return with following keys:
+     *
+     * <ol>
+     *   <li>"join" : Should contain list containing full data for a {@link CustomAudience} object
+     *   <li>"leave" : List of {@link CustomAudience} names that user is intended to be removed from
+     * </ol>
+     *
+     * <p>An example of JSON in response would look like:
+     *
+     * <pre>{@code
+     * {
+     *     "join": [
+     *         {
+     *             "name": "running-shoes",
+     *             "activation_time": 1680603133,
+     *             "expiration_time": 1680803133,
+     *             "user_bidding_signals": {
+     *                 "signal1": "value"
+     *             },
+     *             "trusted_bidding_data": {
+     *                 "trusted_bidding_uri": "https://example-dsp.com/",
+     *                 "trusted_bidding_keys": [
+     *                     "k1",
+     *                     "k2"
+     *                 ]
+     *             },
+     *             "bidding_logic_uri": "https://example-dsp.com/...",
+     *             "ads": [
+     *                 {
+     *                     "render_uri": "https://example-dsp.com/...",
+     *                     "metadata": {},
+     *                     "ad_filters": {
+     *                         "frequency_cap": {
+     *                             "win": [
+     *                                 {
+     *                                     "ad_counter_key": "key1",
+     *                                     "max_count": 2,
+     *                                     "interval_in_seconds": 60
+     *                                 }
+     *                             ],
+     *                             "view": [
+     *                                 {
+     *                                     "ad_counter_key": "key2",
+     *                                     "max_count": 10,
+     *                                     "interval_in_seconds": 3600
+     *                                 }
+     *                             ]
+     *                         },
+     *                         "app_install": {
+     *                             "package_names": [
+     *                                 "package.name.one"
+     *                             ]
+     *                         }
+     *                     }
+     *                 }
+     *             ]
+     *         },
+     *         {}
+     *     ],
+     *     "leave": [
+     *         "tennis_shoes",
+     *         "formal_shirt"
+     *     ]
+     * }
+     * }</pre>
+     *
+     * <p>An attempt to register the user for a custom audience from the same application with the
+     * same combination of {@code buyer} inferred from Update Uri, and {@code name} will cause the
+     * existing custom audience's information to be overwritten, including the list of ads data.
+     *
+     * <p>In case information related to any of the CustomAudience to be joined is malformed, the
+     * deferred update would silently ignore that single Custom Audience
+     *
+     * <p>When removing this API attempts to remove a user from a custom audience by deleting any
+     * existing {@link CustomAudience} identified by owner i.e. calling app, {@code buyer} inferred
+     * from Update Uri, and {@code name}
+     *
+     * <p>Any partial custom audience field set by the caller cannot be overridden by the custom
+     * audience fetched from the {@code updateUri}. Given multiple Custom Audiences could be
+     * returned by a DSP we will match the override restriction based on the names of the Custom
+     * Audiences. A DSP may skip returning a full Custom Audience for any Partial Custom Audience in
+     * request.
+     *
+     * <p>In case the API encounters transient errors while making the network call for update, like
+     * 5xx, connection timeout, rate limit exceeded it would employ retries, with backoff up to a
+     * 'retry limit' number of times. The API would also honor 'retry-after' header specifying the
+     * min amount of seconds by which the next request should be delayed.
+     *
+     * <p>In a scenario where server responds with a '429 status code', signifying 'Too many
+     * requests', API would place the deferred update and other updates for the same requester i.e.
+     * caller package and buyer combination, in a quarantine. The quarantine records would be
+     * referred before making any calls for requesters, and request will only be made once the
+     * quarantine period has expired. The applications can leverage the `retry-after` header to
+     * self-quarantine for traffic management to their servers and prevent being overwhelmed with
+     * requests. The default quarantine value will be set to 30 minutes.
+     *
+     * <p>This call fails with an {@link SecurityException} if
+     *
+     * <ol>
+     *   <li>the {@code ownerPackageName} is not calling app's package name; and/or
+     *   <li>the buyer, inferred from {@code updateUri}, is not authorized to use the API.
+     * </ol>
+     *
+     * <p>This call fails with an {@link IllegalArgumentException} if
+     *
+     * <ol>
+     *   <li>the provided {@code updateUri} is invalid or malformed.
+     *   <li>the provided {@code delayTime} is not within permissible bounds
+     *   <li>the combined size of {@code partialCustomAudience} list is larger than allowed limits
+     * </ol>
+     *
+     * <p>This call fails with {@link LimitExceededException} if the calling package exceeds the
+     * allowed rate limits and is throttled.
+     */
+    @FlaggedApi(FLAG_FLEDGE_SCHEDULE_CUSTOM_AUDIENCE_UPDATE_ENABLED)
+    @RequiresPermission(ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
+    public void scheduleCustomAudienceUpdate(
+            @NonNull ScheduleCustomAudienceUpdateRequest request,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AdServicesOutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+
+        try {
+            final ICustomAudienceService service = getService();
+
+            service.scheduleCustomAudienceUpdate(
+                    new ScheduleCustomAudienceUpdateInput.Builder(
+                                    request.getUpdateUri(),
+                                    getCallerPackageName(),
+                                    request.getMinDelay(),
+                                    request.getPartialCustomAudienceList())
+                            .build(),
+                    new ScheduleCustomAudienceUpdateCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            receiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Internal Error!", e));
+        }
+    }
+
+    private String getCallerPackageName() {
+        SandboxedSdkContext sandboxedSdkContext =
+                SandboxedSdkContextUtils.getAsSandboxedSdkContext(mContext);
+        return sandboxedSdkContext == null
+                ? mContext.getPackageName()
+                : sandboxedSdkContext.getClientPackageName();
+    }
+}
diff --git a/android-35/android/adservices/customaudience/FetchAndJoinCustomAudienceInput.java b/android-35/android/adservices/customaudience/FetchAndJoinCustomAudienceInput.java
new file mode 100644
index 0000000..466dea3
--- /dev/null
+++ b/android-35/android/adservices/customaudience/FetchAndJoinCustomAudienceInput.java
@@ -0,0 +1,341 @@
+/*
+ * 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 android.adservices.customaudience;
+
+import android.adservices.common.AdSelectionSignals;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.AdServicesParcelableUtil;
+
+import java.time.Instant;
+import java.util.Objects;
+
+/**
+ * The input object wrapping the required and optional parameters needed to fetch a {@link
+ * CustomAudience}.
+ *
+ * <p>Refer to {@link FetchAndJoinCustomAudienceRequest} for more information about the parameters.
+ *
+ * @hide
+ */
+public final class FetchAndJoinCustomAudienceInput implements Parcelable {
+    @NonNull private final Uri mFetchUri;
+    @NonNull private final String mCallerPackageName;
+    @Nullable private final String mName;
+    @Nullable private final Instant mActivationTime;
+    @Nullable private final Instant mExpirationTime;
+    @Nullable private final AdSelectionSignals mUserBiddingSignals;
+
+    @NonNull
+    public static final Creator<FetchAndJoinCustomAudienceInput> CREATOR =
+            new Creator<FetchAndJoinCustomAudienceInput>() {
+                @NonNull
+                @Override
+                public FetchAndJoinCustomAudienceInput createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new FetchAndJoinCustomAudienceInput(in);
+                }
+
+                @NonNull
+                @Override
+                public FetchAndJoinCustomAudienceInput[] newArray(int size) {
+                    return new FetchAndJoinCustomAudienceInput[size];
+                }
+            };
+
+    private FetchAndJoinCustomAudienceInput(
+            @NonNull FetchAndJoinCustomAudienceInput.Builder builder) {
+        Objects.requireNonNull(builder);
+
+        mFetchUri = builder.mFetchUri;
+        mName = builder.mName;
+        mActivationTime = builder.mActivationTime;
+        mExpirationTime = builder.mExpirationTime;
+        mUserBiddingSignals = builder.mUserBiddingSignals;
+        mCallerPackageName = builder.mCallerPackageName;
+    }
+
+    private FetchAndJoinCustomAudienceInput(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        mFetchUri = Uri.CREATOR.createFromParcel(in);
+        mName =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, (sourceParcel -> in.readString()));
+        mActivationTime =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, (sourceParcel) -> Instant.ofEpochMilli(sourceParcel.readLong()));
+        mExpirationTime =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, (sourceParcel) -> Instant.ofEpochMilli(sourceParcel.readLong()));
+        mUserBiddingSignals =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, AdSelectionSignals.CREATOR::createFromParcel);
+        mCallerPackageName = in.readString();
+    }
+
+    /**
+     * @return the {@link Uri} from which the custom audience is to be fetched.
+     */
+    @NonNull
+    public Uri getFetchUri() {
+        return mFetchUri;
+    }
+
+    /**
+     * Reference {@link CustomAudience#getName()} for details.
+     *
+     * @return the {@link String} name of the custom audience to join.
+     */
+    @Nullable
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Reference {@link CustomAudience#getActivationTime()} for details.
+     *
+     * @return the {@link Instant} by which joining the custom audience will be delayed.
+     */
+    @Nullable
+    public Instant getActivationTime() {
+        return mActivationTime;
+    }
+
+    /**
+     * Reference {@link CustomAudience#getExpirationTime()} for details.
+     *
+     * @return the {@link Instant} by when the membership to the custom audience will expire.
+     */
+    @Nullable
+    public Instant getExpirationTime() {
+        return mExpirationTime;
+    }
+
+    /**
+     * Reference {@link CustomAudience#getUserBiddingSignals()} for details.
+     *
+     * @return the buyer signals to be consumed by the buyer-provided JavaScript when the custom
+     *     audience participates in an ad selection.
+     */
+    @Nullable
+    public AdSelectionSignals getUserBiddingSignals() {
+        return mUserBiddingSignals;
+    }
+
+    /**
+     * @return the caller app's package name.
+     */
+    @NonNull
+    public String getCallerPackageName() {
+        return mCallerPackageName;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        mFetchUri.writeToParcel(dest, flags);
+        AdServicesParcelableUtil.writeNullableToParcel(dest, mName, Parcel::writeString);
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mActivationTime,
+                (targetParcel, sourceInstant) ->
+                        targetParcel.writeLong(sourceInstant.toEpochMilli()));
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mExpirationTime,
+                (targetParcel, sourceInstant) ->
+                        targetParcel.writeLong(sourceInstant.toEpochMilli()));
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mUserBiddingSignals,
+                (targetParcel, sourceSignals) -> sourceSignals.writeToParcel(targetParcel, flags));
+        dest.writeString(mCallerPackageName);
+    }
+
+    /**
+     * @return {@code true} only if two {@link FetchAndJoinCustomAudienceInput} objects contain the
+     *     same information.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof FetchAndJoinCustomAudienceInput)) return false;
+        FetchAndJoinCustomAudienceInput that = (FetchAndJoinCustomAudienceInput) o;
+        return mFetchUri.equals(that.mFetchUri)
+                && mCallerPackageName.equals(that.mCallerPackageName)
+                && Objects.equals(mName, that.mName)
+                && Objects.equals(mActivationTime, that.mActivationTime)
+                && Objects.equals(mExpirationTime, that.mExpirationTime)
+                && Objects.equals(mUserBiddingSignals, that.mUserBiddingSignals);
+    }
+
+    /**
+     * @return the hash of the {@link FetchAndJoinCustomAudienceInput} object's data.
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mFetchUri,
+                mCallerPackageName,
+                mName,
+                mActivationTime,
+                mExpirationTime,
+                mUserBiddingSignals);
+    }
+
+    /**
+     * @return a human-readable representation of {@link FetchAndJoinCustomAudienceInput}.
+     */
+    @Override
+    public String toString() {
+        return "FetchAndJoinCustomAudienceInput{"
+                + "fetchUri="
+                + mFetchUri
+                + ", name="
+                + mName
+                + ", activationTime="
+                + mActivationTime
+                + ", expirationTime="
+                + mExpirationTime
+                + ", userBiddingSignals="
+                + mUserBiddingSignals
+                + ", callerPackageName="
+                + mCallerPackageName
+                + '}';
+    }
+
+    /**
+     * Builder for {@link FetchAndJoinCustomAudienceInput} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        @NonNull private Uri mFetchUri;
+        @NonNull private String mCallerPackageName;
+        @Nullable private String mName;
+        @Nullable private Instant mActivationTime;
+        @Nullable private Instant mExpirationTime;
+        @Nullable private AdSelectionSignals mUserBiddingSignals;
+
+        /**
+         * Instantiates a {@link FetchAndJoinCustomAudienceInput.Builder} with the {@link Uri} from
+         * which the custom audience is to be fetched and the caller app's package name.
+         *
+         * @throws NullPointerException if any non-null parameter is null.
+         */
+        public Builder(@NonNull Uri fetchUri, @NonNull String callerPackageName) {
+            Objects.requireNonNull(fetchUri);
+            Objects.requireNonNull(callerPackageName);
+
+            this.mFetchUri = fetchUri;
+            this.mCallerPackageName = callerPackageName;
+        }
+
+        /**
+         * Sets the {@link Uri} from which the custom audience is to be fetched.
+         *
+         * <p>See {@link #getFetchUri()} ()} for details.
+         */
+        @NonNull
+        public Builder setFetchUri(@NonNull Uri fetchUri) {
+            Objects.requireNonNull(fetchUri);
+            this.mFetchUri = fetchUri;
+            return this;
+        }
+
+        /**
+         * Sets the {@link String} name of the custom audience to join.
+         *
+         * <p>See {@link #getName()} for details.
+         */
+        @NonNull
+        public Builder setName(@Nullable String name) {
+            this.mName = name;
+            return this;
+        }
+
+        /**
+         * Sets the {@link Instant} by which joining the custom audience will be delayed.
+         *
+         * <p>See {@link #getActivationTime()} for details.
+         */
+        @NonNull
+        public Builder setActivationTime(@Nullable Instant activationTime) {
+            this.mActivationTime = activationTime;
+            return this;
+        }
+
+        /**
+         * Sets the {@link Instant} by when the membership to the custom audience will expire.
+         *
+         * <p>See {@link #getExpirationTime()} for details.
+         */
+        @NonNull
+        public Builder setExpirationTime(@Nullable Instant expirationTime) {
+            this.mExpirationTime = expirationTime;
+            return this;
+        }
+
+        /**
+         * Sets the buyer signals to be consumed by the buyer-provided JavaScript when the custom
+         * audience participates in an ad selection.
+         *
+         * <p>See {@link #getUserBiddingSignals()} for details.
+         */
+        @NonNull
+        public Builder setUserBiddingSignals(@Nullable AdSelectionSignals userBiddingSignals) {
+            this.mUserBiddingSignals = userBiddingSignals;
+            return this;
+        }
+
+        /**
+         * Sets the caller app's package name.
+         *
+         * <p>See {@link #getCallerPackageName()} for details.
+         */
+        @NonNull
+        public Builder setCallerPackageName(@NonNull String callerPackageName) {
+            Objects.requireNonNull(callerPackageName);
+            this.mCallerPackageName = callerPackageName;
+            return this;
+        }
+
+        /**
+         * Builds an instance of a {@link FetchAndJoinCustomAudienceInput}.
+         *
+         * @throws NullPointerException if any non-null parameter is null.
+         */
+        @NonNull
+        public FetchAndJoinCustomAudienceInput build() {
+            Objects.requireNonNull(mFetchUri);
+            Objects.requireNonNull(mCallerPackageName);
+
+            return new FetchAndJoinCustomAudienceInput(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/customaudience/FetchAndJoinCustomAudienceRequest.java b/android-35/android/adservices/customaudience/FetchAndJoinCustomAudienceRequest.java
new file mode 100644
index 0000000..bc97eaa
--- /dev/null
+++ b/android-35/android/adservices/customaudience/FetchAndJoinCustomAudienceRequest.java
@@ -0,0 +1,234 @@
+/*
+ * 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 android.adservices.customaudience;
+
+import android.adservices.common.AdSelectionSignals;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+
+import java.time.Instant;
+import java.util.Objects;
+
+/**
+ * The request object wrapping the required and optional parameters needed to fetch a {@link
+ * CustomAudience}.
+ *
+ * <p>{@code fetchUri} is the only required parameter. It represents the URI to fetch a custom
+ * audience from. {@code name}, {@code activationTime}, {@code expirationTime} and {@code
+ * userBiddingSignals} are optional parameters. They represent a partial custom audience which can
+ * be used by the caller to inform the choice of the custom audience the user should be added to.
+ * Any field set by the caller cannot be overridden by the custom audience fetched from the {@code
+ * fetchUri}. For more information about each field refer to {@link CustomAudience}.
+ */
+public final class FetchAndJoinCustomAudienceRequest {
+    @NonNull private final Uri mFetchUri;
+    @Nullable private final String mName;
+    @Nullable private final Instant mActivationTime;
+    @Nullable private final Instant mExpirationTime;
+    @Nullable private final AdSelectionSignals mUserBiddingSignals;
+
+    private FetchAndJoinCustomAudienceRequest(
+            @NonNull FetchAndJoinCustomAudienceRequest.Builder builder) {
+        Objects.requireNonNull(builder.mFetchUri);
+
+        mFetchUri = builder.mFetchUri;
+        mName = builder.mName;
+        mActivationTime = builder.mActivationTime;
+        mExpirationTime = builder.mExpirationTime;
+        mUserBiddingSignals = builder.mUserBiddingSignals;
+    }
+
+    /**
+     * @return the {@link Uri} from which the custom audience is to be fetched.
+     */
+    @NonNull
+    public Uri getFetchUri() {
+        return mFetchUri;
+    }
+
+    /**
+     * Reference {@link CustomAudience#getName()} for details.
+     *
+     * @return the {@link String} name of the custom audience to join.
+     */
+    @Nullable
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Reference {@link CustomAudience#getActivationTime()} for details.
+     *
+     * @return the {@link Instant} by which joining the custom audience will be delayed.
+     */
+    @Nullable
+    public Instant getActivationTime() {
+        return mActivationTime;
+    }
+
+    /**
+     * Reference {@link CustomAudience#getExpirationTime()} for details.
+     *
+     * @return the {@link Instant} by when the membership to the custom audience will expire.
+     */
+    @Nullable
+    public Instant getExpirationTime() {
+        return mExpirationTime;
+    }
+
+    /**
+     * Reference {@link CustomAudience#getUserBiddingSignals()} for details.
+     *
+     * @return the buyer signals to be consumed by the buyer-provided JavaScript when the custom
+     *     audience participates in an ad selection.
+     */
+    @Nullable
+    public AdSelectionSignals getUserBiddingSignals() {
+        return mUserBiddingSignals;
+    }
+
+    /**
+     * @return {@code true} only if two {@link FetchAndJoinCustomAudienceRequest} objects contain
+     *     the same information.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof FetchAndJoinCustomAudienceRequest)) return false;
+        FetchAndJoinCustomAudienceRequest that = (FetchAndJoinCustomAudienceRequest) o;
+        return mFetchUri.equals(that.mFetchUri)
+                && Objects.equals(mName, that.mName)
+                && Objects.equals(mActivationTime, that.mActivationTime)
+                && Objects.equals(mExpirationTime, that.mExpirationTime)
+                && Objects.equals(mUserBiddingSignals, that.mUserBiddingSignals);
+    }
+
+    /**
+     * @return the hash of the {@link FetchAndJoinCustomAudienceRequest} object's data.
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mFetchUri, mName, mActivationTime, mExpirationTime, mUserBiddingSignals);
+    }
+
+    /**
+     * @return a human-readable representation of {@link FetchAndJoinCustomAudienceRequest}.
+     */
+    @Override
+    public String toString() {
+        return "FetchAndJoinCustomAudienceRequest{"
+                + "fetchUri="
+                + mFetchUri
+                + ", name="
+                + mName
+                + ", activationTime="
+                + mActivationTime
+                + ", expirationTime="
+                + mExpirationTime
+                + ", userBiddingSignals="
+                + mUserBiddingSignals
+                + '}';
+    }
+
+    /** Builder for {@link FetchAndJoinCustomAudienceRequest} objects. */
+    public static final class Builder {
+        @NonNull private Uri mFetchUri;
+        @Nullable private String mName;
+        @Nullable private Instant mActivationTime;
+        @Nullable private Instant mExpirationTime;
+        @Nullable private AdSelectionSignals mUserBiddingSignals;
+
+        /**
+         * Instantiates a {@link FetchAndJoinCustomAudienceRequest.Builder} with the {@link Uri}
+         * from which the custom audience is to be fetched.
+         */
+        public Builder(@NonNull Uri fetchUri) {
+            Objects.requireNonNull(fetchUri);
+            this.mFetchUri = fetchUri;
+        }
+
+        /**
+         * Sets the {@link Uri} from which the custom audience is to be fetched.
+         *
+         * <p>See {@link #getFetchUri()} ()} for details.
+         */
+        @NonNull
+        public Builder setFetchUri(@NonNull Uri fetchUri) {
+            Objects.requireNonNull(fetchUri);
+            this.mFetchUri = fetchUri;
+            return this;
+        }
+
+        /**
+         * Sets the {@link String} name of the custom audience to join.
+         *
+         * <p>See {@link #getName()} for details.
+         */
+        @NonNull
+        public Builder setName(@Nullable String name) {
+            this.mName = name;
+            return this;
+        }
+
+        /**
+         * Sets the {@link Instant} by which joining the custom audience will be delayed.
+         *
+         * <p>See {@link #getActivationTime()} for details.
+         */
+        @NonNull
+        public Builder setActivationTime(@Nullable Instant activationTime) {
+            this.mActivationTime = activationTime;
+            return this;
+        }
+
+        /**
+         * Sets the {@link Instant} by when the membership to the custom audience will expire.
+         *
+         * <p>See {@link #getExpirationTime()} for details.
+         */
+        @NonNull
+        public Builder setExpirationTime(@Nullable Instant expirationTime) {
+            this.mExpirationTime = expirationTime;
+            return this;
+        }
+
+        /**
+         * Sets the buyer signals to be consumed by the buyer-provided JavaScript when the custom
+         * audience participates in an ad selection.
+         *
+         * <p>See {@link #getUserBiddingSignals()} for details.
+         */
+        @NonNull
+        public Builder setUserBiddingSignals(@Nullable AdSelectionSignals userBiddingSignals) {
+            this.mUserBiddingSignals = userBiddingSignals;
+            return this;
+        }
+
+        /**
+         * Builds an instance of a {@link FetchAndJoinCustomAudienceRequest}.
+         *
+         * @throws NullPointerException if any non-null parameter is null.
+         */
+        @NonNull
+        public FetchAndJoinCustomAudienceRequest build() {
+            Objects.requireNonNull(mFetchUri);
+            return new FetchAndJoinCustomAudienceRequest(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/customaudience/JoinCustomAudienceRequest.java b/android-35/android/adservices/customaudience/JoinCustomAudienceRequest.java
new file mode 100644
index 0000000..7a5f59c
--- /dev/null
+++ b/android-35/android/adservices/customaudience/JoinCustomAudienceRequest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.customaudience;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Objects;
+
+/**
+ * The request object to join a custom audience.
+ */
+public class JoinCustomAudienceRequest {
+    @NonNull
+    private final CustomAudience mCustomAudience;
+
+    private JoinCustomAudienceRequest(@NonNull JoinCustomAudienceRequest.Builder builder) {
+        mCustomAudience = builder.mCustomAudience;
+    }
+
+    /**
+     * Returns the custom audience to join.
+     */
+    @NonNull
+    public CustomAudience getCustomAudience() {
+        return mCustomAudience;
+    }
+
+    /**
+     * Checks whether two {@link JoinCustomAudienceRequest} objects contain the same information.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof JoinCustomAudienceRequest)) return false;
+        JoinCustomAudienceRequest that = (JoinCustomAudienceRequest) o;
+        return mCustomAudience.equals(that.mCustomAudience);
+    }
+
+    /**
+     * Returns the hash of the {@link JoinCustomAudienceRequest} object's data.
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCustomAudience);
+    }
+
+    /** Builder for {@link JoinCustomAudienceRequest} objects. */
+    public static final class Builder {
+        @Nullable private CustomAudience mCustomAudience;
+
+        public Builder() {
+        }
+
+        /**
+         * Sets the custom audience to join.
+         *
+         * <p>See {@link #getCustomAudience()} for more information.
+         */
+        @NonNull
+        public JoinCustomAudienceRequest.Builder setCustomAudience(
+                @NonNull CustomAudience customAudience) {
+            Objects.requireNonNull(customAudience);
+            mCustomAudience = customAudience;
+            return this;
+        }
+
+        /**
+         * Builds an instance of a {@link JoinCustomAudienceRequest}.
+         *
+         * @throws NullPointerException if any non-null parameter is null
+         */
+        @NonNull
+        public JoinCustomAudienceRequest build() {
+            Objects.requireNonNull(mCustomAudience, "The custom audience has not been provided");
+
+            return new JoinCustomAudienceRequest(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/customaudience/LeaveCustomAudienceRequest.java b/android-35/android/adservices/customaudience/LeaveCustomAudienceRequest.java
new file mode 100644
index 0000000..b7d77ef
--- /dev/null
+++ b/android-35/android/adservices/customaudience/LeaveCustomAudienceRequest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.customaudience;
+
+import android.adservices.common.AdTechIdentifier;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Objects;
+
+/** The request object is used to leave a custom audience. */
+public final class LeaveCustomAudienceRequest {
+    @NonNull private final AdTechIdentifier mBuyer;
+    @NonNull private final String mName;
+
+    private LeaveCustomAudienceRequest(@NonNull LeaveCustomAudienceRequest.Builder builder) {
+        mBuyer = builder.mBuyer;
+        mName = builder.mName;
+    }
+
+    /**
+     * Gets the buyer's {@link AdTechIdentifier}, as identified by a domain in the form
+     * "buyerexample.com".
+     *
+     * @return an {@link AdTechIdentifier} containing the custom audience's buyer's domain
+     */
+    @NonNull
+    public AdTechIdentifier getBuyer() {
+        return mBuyer;
+    }
+
+    /**
+     * Gets the arbitrary string provided by the owner and buyer on creation of the {@link
+     * CustomAudience} object that represents a single custom audience.
+     *
+     * @return the String name of the custom audience
+     */
+    @NonNull
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Checks whether two {@link LeaveCustomAudienceRequest} objects contain the same information.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof LeaveCustomAudienceRequest)) return false;
+        LeaveCustomAudienceRequest that = (LeaveCustomAudienceRequest) o;
+        return mBuyer.equals(that.mBuyer) && mName.equals(that.mName);
+    }
+
+    /**
+     * Returns the hash of the {@link LeaveCustomAudienceRequest} object's data.
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mBuyer, mName);
+    }
+
+    /** Builder for {@link LeaveCustomAudienceRequest} objects. */
+    public static final class Builder {
+        @Nullable private AdTechIdentifier mBuyer;
+        @Nullable private String mName;
+
+        public Builder() {}
+
+        /**
+         * Sets the buyer {@link AdTechIdentifier}.
+         *
+         * <p>See {@link #getBuyer()} for more information.
+         */
+        @NonNull
+        public LeaveCustomAudienceRequest.Builder setBuyer(@NonNull AdTechIdentifier buyer) {
+            Objects.requireNonNull(buyer);
+            mBuyer = buyer;
+            return this;
+        }
+
+        /**
+         * Sets the {@link CustomAudience} object's name.
+         * <p>
+         * See {@link #getName()} for more information.
+         */
+        @NonNull
+        public LeaveCustomAudienceRequest.Builder setName(@NonNull String name) {
+            Objects.requireNonNull(name);
+            mName = name;
+            return this;
+        }
+
+        /**
+         * Builds an instance of a {@link LeaveCustomAudienceRequest}.
+         *
+         * @throws NullPointerException if any non-null parameter is null
+         */
+        @NonNull
+        public LeaveCustomAudienceRequest build() {
+            Objects.requireNonNull(mBuyer);
+            Objects.requireNonNull(mName);
+
+            return new LeaveCustomAudienceRequest(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/customaudience/PartialCustomAudience.java b/android-35/android/adservices/customaudience/PartialCustomAudience.java
new file mode 100644
index 0000000..36c194b
--- /dev/null
+++ b/android-35/android/adservices/customaudience/PartialCustomAudience.java
@@ -0,0 +1,265 @@
+/*
+ * 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 android.adservices.customaudience;
+
+import static com.android.adservices.flags.Flags.FLAG_FLEDGE_SCHEDULE_CUSTOM_AUDIENCE_UPDATE_ENABLED;
+
+import android.adservices.common.AdSelectionSignals;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.AdServicesParcelableUtil;
+
+import java.time.Instant;
+import java.util.Objects;
+
+/**
+ * Represents a partial custom audience that is passed along to DSP, when scheduling a delayed
+ * update for Custom Audience. Any field set by the caller cannot be overridden by the custom
+ * audience fetched from the {@code updateUri}
+ *
+ * <p>Given multiple Custom Audiences could be returned by DSP we will match the override
+ * restriction based on the name of Custom Audience. Thus name would be a required field.
+ *
+ * <p>Other nullable fields will not be overridden if left null
+ *
+ * <p>For more information about each field refer to {@link CustomAudience}.
+ */
+@FlaggedApi(FLAG_FLEDGE_SCHEDULE_CUSTOM_AUDIENCE_UPDATE_ENABLED)
+public final class PartialCustomAudience implements Parcelable {
+    @NonNull private final String mName;
+    @Nullable private final Instant mActivationTime;
+    @Nullable private final Instant mExpirationTime;
+    @Nullable private final AdSelectionSignals mUserBiddingSignals;
+
+    private PartialCustomAudience(@NonNull PartialCustomAudience.Builder builder) {
+        Objects.requireNonNull(builder);
+
+        mName = builder.mName;
+        mActivationTime = builder.mActivationTime;
+        mExpirationTime = builder.mExpirationTime;
+        mUserBiddingSignals = builder.mUserBiddingSignals;
+    }
+
+    @NonNull
+    public static final Creator<PartialCustomAudience> CREATOR =
+            new Creator<PartialCustomAudience>() {
+                @NonNull
+                @Override
+                public PartialCustomAudience createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new PartialCustomAudience(in);
+                }
+
+                @NonNull
+                @Override
+                public PartialCustomAudience[] newArray(int size) {
+                    return new PartialCustomAudience[size];
+                }
+            };
+
+    private PartialCustomAudience(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        mName = in.readString();
+        mActivationTime =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, (sourceParcel) -> Instant.ofEpochMilli(sourceParcel.readLong()));
+        mExpirationTime =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, (sourceParcel) -> Instant.ofEpochMilli(sourceParcel.readLong()));
+        mUserBiddingSignals =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, AdSelectionSignals.CREATOR::createFromParcel);
+    }
+
+    /**
+     * Reference {@link CustomAudience#getName()} for details.
+     *
+     * @return the {@link String} name of the custom audience to join.
+     */
+    @NonNull
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Reference {@link CustomAudience#getActivationTime()} for details. Will not be overridden if
+     * left null.
+     *
+     * @return the {@link Instant} by which joining the custom audience will be delayed.
+     */
+    @Nullable
+    public Instant getActivationTime() {
+        return mActivationTime;
+    }
+
+    /**
+     * Reference {@link CustomAudience#getExpirationTime()} for details. Will not be overridden if
+     * left null.
+     *
+     * @return the {@link Instant} by when the membership to the custom audience will expire.
+     */
+    @Nullable
+    public Instant getExpirationTime() {
+        return mExpirationTime;
+    }
+
+    /**
+     * Reference {@link CustomAudience#getUserBiddingSignals()} for details. Will not be overridden
+     * if left null.
+     *
+     * @return the buyer signals to be consumed by the buyer-provided JavaScript when the custom
+     *     audience participates in an ad selection.
+     */
+    @Nullable
+    public AdSelectionSignals getUserBiddingSignals() {
+        return mUserBiddingSignals;
+    }
+
+    /**
+     * @return the hash of the {@link PartialCustomAudience} object's data.
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mName, mActivationTime, mExpirationTime, mUserBiddingSignals);
+    }
+
+    /**
+     * @return {@code true} only if two {@link PartialCustomAudience} objects contain the same
+     *     information.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof PartialCustomAudience)) return false;
+        PartialCustomAudience that = (PartialCustomAudience) o;
+        return Objects.equals(mName, that.mName)
+                && Objects.equals(mActivationTime, that.mActivationTime)
+                && Objects.equals(mExpirationTime, that.mExpirationTime)
+                && Objects.equals(mUserBiddingSignals, that.mUserBiddingSignals);
+    }
+
+    /**
+     * @return a human-readable representation of {@link PartialCustomAudience}.
+     */
+    @Override
+    public String toString() {
+        return "PartialCustomAudience {"
+                + "name="
+                + mName
+                + ", activationTime="
+                + mActivationTime
+                + ", expirationTime="
+                + mExpirationTime
+                + ", userBiddingSignals="
+                + mUserBiddingSignals
+                + '}';
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        dest.writeString(mName);
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mActivationTime,
+                (targetParcel, sourceInstant) ->
+                        targetParcel.writeLong(sourceInstant.toEpochMilli()));
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mExpirationTime,
+                (targetParcel, sourceInstant) ->
+                        targetParcel.writeLong(sourceInstant.toEpochMilli()));
+        AdServicesParcelableUtil.writeNullableToParcel(
+                dest,
+                mUserBiddingSignals,
+                (targetParcel, sourceSignals) -> sourceSignals.writeToParcel(targetParcel, flags));
+    }
+
+    /** Builder for {@link PartialCustomAudience} objects. */
+    public static final class Builder {
+        @NonNull private String mName;
+        @Nullable private Instant mActivationTime;
+        @Nullable private Instant mExpirationTime;
+        @Nullable private AdSelectionSignals mUserBiddingSignals;
+
+        /**
+         * Instantiates a {@link PartialCustomAudience.Builder} with a {@link String} name for which
+         * this Partial Custom Audience will be updated
+         */
+        public Builder(@NonNull String name) {
+            Objects.requireNonNull(name);
+            this.mName = name;
+        }
+
+        /**
+         * Sets the {@link Instant} by which joining the custom audience will be delayed.
+         *
+         * <p>See {@link #getActivationTime()} for details.
+         */
+        @NonNull
+        public PartialCustomAudience.Builder setActivationTime(@Nullable Instant activationTime) {
+            this.mActivationTime = activationTime;
+            return this;
+        }
+
+        /**
+         * Sets the {@link Instant} by when the membership to the custom audience will expire.
+         *
+         * <p>See {@link #getExpirationTime()} for details.
+         */
+        @NonNull
+        public PartialCustomAudience.Builder setExpirationTime(@Nullable Instant expirationTime) {
+            this.mExpirationTime = expirationTime;
+            return this;
+        }
+
+        /**
+         * Sets the buyer signals to be consumed by the buyer-provided JavaScript when the custom
+         * audience participates in an ad selection.
+         *
+         * <p>See {@link #getUserBiddingSignals()} for details.
+         */
+        @NonNull
+        public PartialCustomAudience.Builder setUserBiddingSignals(
+                @Nullable AdSelectionSignals userBiddingSignals) {
+            this.mUserBiddingSignals = userBiddingSignals;
+            return this;
+        }
+
+        /**
+         * Builds an instance of a {@link FetchAndJoinCustomAudienceRequest}.
+         *
+         * @throws NullPointerException if any non-null parameter is null.
+         */
+        @NonNull
+        public PartialCustomAudience build() {
+            Objects.requireNonNull(mName);
+            return new PartialCustomAudience(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/customaudience/RemoveCustomAudienceOverrideRequest.java b/android-35/android/adservices/customaudience/RemoveCustomAudienceOverrideRequest.java
new file mode 100644
index 0000000..2996129
--- /dev/null
+++ b/android-35/android/adservices/customaudience/RemoveCustomAudienceOverrideRequest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.customaudience;
+
+import android.adservices.common.AdTechIdentifier;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.OutcomeReceiver;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * This POJO represents the {@link TestCustomAudienceManager#removeCustomAudienceRemoteInfoOverride(
+ * RemoveCustomAudienceOverrideRequest, Executor, OutcomeReceiver)} request.
+ *
+ * <p>It contains fields {@code buyer} and {@code name} which will serve as the identifier for the
+ * overrides to be removed.
+ */
+public class RemoveCustomAudienceOverrideRequest {
+    @NonNull private final AdTechIdentifier mBuyer;
+    @NonNull private final String mName;
+
+    public RemoveCustomAudienceOverrideRequest(
+            @NonNull AdTechIdentifier buyer,
+            @NonNull String name) {
+        mBuyer = buyer;
+        mName = name;
+    }
+
+    /** @return an {@link AdTechIdentifier} representing the buyer */
+    @NonNull
+    public AdTechIdentifier getBuyer() {
+        return mBuyer;
+    }
+
+    /** @return name of the custom audience being overridden */
+    @NonNull
+    public String getName() {
+        return mName;
+    }
+
+    /** Builder for {@link RemoveCustomAudienceOverrideRequest} objects. */
+    public static final class Builder {
+        @Nullable private AdTechIdentifier mBuyer;
+        @Nullable private String mName;
+
+        public Builder() {}
+
+        /** Sets the buyer {@link AdTechIdentifier} for the custom audience. */
+        @NonNull
+        public RemoveCustomAudienceOverrideRequest.Builder setBuyer(
+                @NonNull AdTechIdentifier buyer) {
+            Objects.requireNonNull(buyer);
+
+            this.mBuyer = buyer;
+            return this;
+        }
+
+        /** Sets the name for the custom audience that was overridden. */
+        @NonNull
+        public RemoveCustomAudienceOverrideRequest.Builder setName(@NonNull String name) {
+            Objects.requireNonNull(name);
+
+            this.mName = name;
+            return this;
+        }
+
+        /** Builds a {@link RemoveCustomAudienceOverrideRequest} instance. */
+        @NonNull
+        public RemoveCustomAudienceOverrideRequest build() {
+            Objects.requireNonNull(mBuyer);
+            Objects.requireNonNull(mName);
+
+            return new RemoveCustomAudienceOverrideRequest(mBuyer, mName);
+        }
+    }
+}
diff --git a/android-35/android/adservices/customaudience/ScheduleCustomAudienceUpdateInput.java b/android-35/android/adservices/customaudience/ScheduleCustomAudienceUpdateInput.java
new file mode 100644
index 0000000..50714fb
--- /dev/null
+++ b/android-35/android/adservices/customaudience/ScheduleCustomAudienceUpdateInput.java
@@ -0,0 +1,254 @@
+/*
+ * 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 android.adservices.customaudience;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Allows AdTechs to provide an Update Uri, and the minimum Delay Time to schedule the update.
+ *
+ * <p>Refer to {@link ScheduleCustomAudienceUpdateRequest} for more information about the
+ * parameters.
+ *
+ * @hide
+ */
+public final class ScheduleCustomAudienceUpdateInput implements Parcelable {
+    @NonNull private final Uri mUpdateUri;
+    @NonNull private final String mCallerPackageName;
+    @NonNull private final Duration mMinDelay;
+    @NonNull private final List<PartialCustomAudience> mPartialCustomAudienceList;
+
+    @NonNull
+    public static final Creator<ScheduleCustomAudienceUpdateInput> CREATOR =
+            new Creator<ScheduleCustomAudienceUpdateInput>() {
+                @NonNull
+                @Override
+                public ScheduleCustomAudienceUpdateInput createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new ScheduleCustomAudienceUpdateInput(in);
+                }
+
+                @NonNull
+                @Override
+                public ScheduleCustomAudienceUpdateInput[] newArray(int size) {
+                    return new ScheduleCustomAudienceUpdateInput[size];
+                }
+            };
+
+    private ScheduleCustomAudienceUpdateInput(
+            @NonNull ScheduleCustomAudienceUpdateInput.Builder builder) {
+        Objects.requireNonNull(builder);
+
+        mUpdateUri = builder.mUpdateUri;
+        mCallerPackageName = builder.mCallerPackageName;
+        mMinDelay = builder.mMinDelay;
+        mPartialCustomAudienceList = builder.mPartialCustomAudienceList;
+    }
+
+    private ScheduleCustomAudienceUpdateInput(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        mUpdateUri = Uri.CREATOR.createFromParcel(in);
+        mCallerPackageName = in.readString();
+        mMinDelay = Duration.ofMillis(in.readLong());
+        mPartialCustomAudienceList = in.createTypedArrayList(PartialCustomAudience.CREATOR);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        mUpdateUri.writeToParcel(dest, flags);
+        dest.writeString(mCallerPackageName);
+        dest.writeLong(mMinDelay.toMillis());
+        dest.writeTypedList(mPartialCustomAudienceList);
+    }
+
+    /** Returns the {@link Uri} from which the Custom Audience is to be fetched */
+    @NonNull
+    public Uri getUpdateUri() {
+        return mUpdateUri;
+    }
+
+    /** Returns the caller app's package name. */
+    @NonNull
+    public String getCallerPackageName() {
+        return mCallerPackageName;
+    }
+
+    /** Returns the {@link Duration} min time duration for which the update is deferred */
+    @NonNull
+    public Duration getMinDelay() {
+        return mMinDelay;
+    }
+
+    /**
+     * Returns the list of {@link PartialCustomAudience} which are sent along with the request to
+     * download the update for Custom Audience
+     */
+    @NonNull
+    public List<PartialCustomAudience> getPartialCustomAudienceList() {
+        return mPartialCustomAudienceList;
+    }
+
+    /** Returns the hash of {@link ScheduleCustomAudienceUpdateInput} object's data. */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUpdateUri, mCallerPackageName, mMinDelay, mPartialCustomAudienceList);
+    }
+
+    /**
+     * @return {@code true} only if two {@link ScheduleCustomAudienceUpdateInput} objects contain
+     *     the same information.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ScheduleCustomAudienceUpdateInput)) return false;
+        ScheduleCustomAudienceUpdateInput that = (ScheduleCustomAudienceUpdateInput) o;
+        return mUpdateUri.equals(that.mUpdateUri)
+                && mCallerPackageName.equals(that.mCallerPackageName)
+                && mMinDelay.equals(that.mMinDelay)
+                && Objects.equals(mPartialCustomAudienceList, that.mPartialCustomAudienceList);
+    }
+
+    /**
+     * @return a human-readable representation of {@link ScheduleCustomAudienceUpdateInput}.
+     */
+    @Override
+    public String toString() {
+        return "ScheduleCustomAudienceUpdateInput {"
+                + "updateUri="
+                + mUpdateUri
+                + ", callerPackageName="
+                + mCallerPackageName
+                + ", delayTimeMinutes="
+                + mMinDelay.toMinutes()
+                + ", partialCustomAudienceList="
+                + mPartialCustomAudienceList
+                + '}';
+    }
+
+    /** Builder for {@link ScheduleCustomAudienceUpdateInput} objects. */
+    public static final class Builder {
+        @NonNull private Uri mUpdateUri;
+        @NonNull private Duration mMinDelay;
+        @NonNull private String mCallerPackageName;
+        @NonNull private List<PartialCustomAudience> mPartialCustomAudienceList;
+
+        /**
+         * Instantiates a {@link ScheduleCustomAudienceUpdateInput.Builder} with the following
+         *
+         * @param updateUri from which the update for Custom Audience is to be fetched
+         * @param callerPackageName the caller app's package name
+         * @param minDelay minimum delay time duration for which the update is to be deferred
+         * @param partialCustomAudienceList list of partial Custom Audiences that are overridden by
+         *     MMP on update
+         */
+        public Builder(
+                @NonNull Uri updateUri,
+                @NonNull String callerPackageName,
+                @NonNull Duration minDelay,
+                @NonNull List<PartialCustomAudience> partialCustomAudienceList) {
+            Objects.requireNonNull(updateUri);
+            Objects.requireNonNull(callerPackageName);
+            Objects.requireNonNull(minDelay);
+            Objects.requireNonNull(partialCustomAudienceList);
+
+            mUpdateUri = updateUri;
+            mCallerPackageName = callerPackageName;
+            mMinDelay = minDelay;
+            mPartialCustomAudienceList = partialCustomAudienceList;
+        }
+
+        /**
+         * Sets the {@link Uri} from which the update for Custom Audience is to be fetched
+         *
+         * <p>See {@link #getUpdateUri()} for details
+         */
+        @NonNull
+        public Builder setUpdateUri(@NonNull Uri updateUri) {
+            Objects.requireNonNull(updateUri);
+            this.mUpdateUri = updateUri;
+            return this;
+        }
+
+        /**
+         * Sets the caller app's package name.
+         *
+         * <p>See {@link #getCallerPackageName()} for details.
+         */
+        @NonNull
+        public Builder setCallerPackageName(@NonNull String callerPackageName) {
+            Objects.requireNonNull(callerPackageName);
+            this.mCallerPackageName = callerPackageName;
+            return this;
+        }
+
+        /**
+         * Sets the {@link Duration} , min time for which the update is to be deferred
+         *
+         * <p>See {@link #getMinDelay()} for more details
+         */
+        @NonNull
+        public Builder setMinDelay(@NonNull Duration minDelay) {
+            Objects.requireNonNull(minDelay);
+            this.mMinDelay = minDelay;
+            return this;
+        }
+
+        /**
+         * Sets list of Partial Custom Audiences that are sent to the DSP server when making a
+         * request to download updates for Custom Audience
+         *
+         * <p>See {@link #getPartialCustomAudienceList()} for more details
+         */
+        @NonNull
+        public Builder setPartialCustomAudienceList(
+                @NonNull List<PartialCustomAudience> partialCustomAudiences) {
+            this.mPartialCustomAudienceList = partialCustomAudiences;
+            return this;
+        }
+
+        /**
+         * Builds an instance of {@link ScheduleCustomAudienceUpdateInput}
+         *
+         * @throws NullPointerException if any of the non-null parameters is null
+         */
+        @NonNull
+        public ScheduleCustomAudienceUpdateInput build() {
+            Objects.requireNonNull(mUpdateUri);
+            Objects.requireNonNull(mCallerPackageName);
+            Objects.requireNonNull(mMinDelay);
+            Objects.requireNonNull(mPartialCustomAudienceList);
+
+            return new ScheduleCustomAudienceUpdateInput(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/customaudience/ScheduleCustomAudienceUpdateRequest.java b/android-35/android/adservices/customaudience/ScheduleCustomAudienceUpdateRequest.java
new file mode 100644
index 0000000..3038801
--- /dev/null
+++ b/android-35/android/adservices/customaudience/ScheduleCustomAudienceUpdateRequest.java
@@ -0,0 +1,181 @@
+/*
+ * 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 android.adservices.customaudience;
+
+import static com.android.adservices.flags.Flags.FLAG_FLEDGE_SCHEDULE_CUSTOM_AUDIENCE_UPDATE_ENABLED;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.net.Uri;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The request object wrapping the required and optional parameters to schedule a deferred update
+ * for Custom Audience on device. Allows AdTechs to provide an Update Uri, and the minimum Delay
+ * Time to schedule the update.
+ */
+@FlaggedApi(FLAG_FLEDGE_SCHEDULE_CUSTOM_AUDIENCE_UPDATE_ENABLED)
+public final class ScheduleCustomAudienceUpdateRequest {
+    @NonNull private final Uri mUpdateUri;
+    @NonNull private final Duration mMinDelay;
+    @NonNull private final List<PartialCustomAudience> mPartialCustomAudienceList;
+
+    private ScheduleCustomAudienceUpdateRequest(
+            @NonNull ScheduleCustomAudienceUpdateRequest.Builder builder) {
+        Objects.requireNonNull(builder);
+
+        this.mUpdateUri = builder.mUpdateUri;
+        this.mMinDelay = builder.mMinDelay;
+        this.mPartialCustomAudienceList = builder.mPartialCustomAudienceList;
+    }
+
+    /** Returns the {@link Uri} from which the Custom Audience is to be fetched */
+    @NonNull
+    public Uri getUpdateUri() {
+        return mUpdateUri;
+    }
+
+    /** Returns the {@link Duration} min time duration for which the update is deferred */
+    @NonNull
+    public Duration getMinDelay() {
+        return mMinDelay;
+    }
+
+    /**
+     * Returns the list of {@link PartialCustomAudience} which are sent along with the request to
+     * download the update for Custom Audience
+     */
+    @NonNull
+    public List<PartialCustomAudience> getPartialCustomAudienceList() {
+        return mPartialCustomAudienceList;
+    }
+
+    /** Returns the hash of {@link ScheduleCustomAudienceUpdateRequest} object's data. */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUpdateUri, mMinDelay, mPartialCustomAudienceList);
+    }
+
+    /**
+     * @return {@code true} only if two {@link ScheduleCustomAudienceUpdateRequest} objects contain
+     *     the same information.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ScheduleCustomAudienceUpdateRequest)) return false;
+        ScheduleCustomAudienceUpdateRequest that = (ScheduleCustomAudienceUpdateRequest) o;
+        return mUpdateUri.equals(that.mUpdateUri)
+                && mMinDelay.equals(that.mMinDelay)
+                && Objects.equals(mPartialCustomAudienceList, that.mPartialCustomAudienceList);
+    }
+
+    /**
+     * @return a human-readable representation of {@link ScheduleCustomAudienceUpdateRequest}.
+     */
+    @Override
+    public String toString() {
+        return "ScheduleCustomAudienceUpdateRequest {"
+                + "updateUri="
+                + mUpdateUri
+                + ", delayTimeMinutes="
+                + mMinDelay.toMinutes()
+                + ", partialCustomAudienceList="
+                + mPartialCustomAudienceList
+                + '}';
+    }
+
+    /** Builder for {@link ScheduleCustomAudienceUpdateRequest} objects. */
+    public static final class Builder {
+        @NonNull private Uri mUpdateUri;
+        @NonNull private Duration mMinDelay;
+        @NonNull private List<PartialCustomAudience> mPartialCustomAudienceList;
+
+        /**
+         * Instantiates a {@link ScheduleCustomAudienceUpdateRequest.Builder} with the following
+         *
+         * @param updateUri from which the update for Custom Audience is to be fetched
+         * @param minDelay minimum delay time duration for which the update is to be deferred
+         */
+        public Builder(
+                @NonNull Uri updateUri,
+                @NonNull Duration minDelay,
+                @NonNull List<PartialCustomAudience> partialCustomAudienceList) {
+            Objects.requireNonNull(updateUri);
+            Objects.requireNonNull(minDelay);
+            Objects.requireNonNull(partialCustomAudienceList);
+
+            this.mUpdateUri = updateUri;
+            this.mMinDelay = minDelay;
+            this.mPartialCustomAudienceList = partialCustomAudienceList;
+        }
+
+        /**
+         * Sets the {@link Uri} from which the update for Custom Audience is to be fetched
+         *
+         * <p>See {@link #getUpdateUri()} for details
+         */
+        @NonNull
+        public Builder setUpdateUri(@NonNull Uri updateUri) {
+            Objects.requireNonNull(updateUri);
+            this.mUpdateUri = updateUri;
+            return this;
+        }
+
+        /**
+         * Sets the {@link Duration} , min time for which the update is to be deferred
+         *
+         * <p>See {@link #getMinDelay()} for more details
+         */
+        @NonNull
+        public Builder setMinDelay(@NonNull Duration minDelay) {
+            Objects.requireNonNull(minDelay);
+            this.mMinDelay = minDelay;
+            return this;
+        }
+
+        /**
+         * Sets list of Partial Custom Audiences that are sent to the DSP server when making a
+         * request to download updates for Custom Audience
+         *
+         * <p>See {@link #getPartialCustomAudienceList()} for more details
+         */
+        @NonNull
+        public Builder setPartialCustomAudienceList(
+                @NonNull List<PartialCustomAudience> partialCustomAudiences) {
+            this.mPartialCustomAudienceList = partialCustomAudiences;
+            return this;
+        }
+
+        /**
+         * Builds an instance of {@link ScheduleCustomAudienceUpdateRequest}
+         *
+         * @throws NullPointerException if any of the non-null parameters is null
+         */
+        @NonNull
+        public ScheduleCustomAudienceUpdateRequest build() {
+            Objects.requireNonNull(mUpdateUri);
+            Objects.requireNonNull(mMinDelay);
+            Objects.requireNonNull(mPartialCustomAudienceList);
+
+            return new ScheduleCustomAudienceUpdateRequest(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/customaudience/TestCustomAudienceManager.java b/android-35/android/adservices/customaudience/TestCustomAudienceManager.java
new file mode 100644
index 0000000..26384d9
--- /dev/null
+++ b/android-35/android/adservices/customaudience/TestCustomAudienceManager.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.customaudience;
+
+import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE;
+
+import android.adservices.common.AdServicesStatusUtils;
+import android.adservices.common.FledgeErrorResponse;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.os.Build;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.adservices.LoggerFactory;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/** TestCustomAudienceManager provides APIs for app and ad-SDKs to test custom audiences. */
+@RequiresApi(Build.VERSION_CODES.S)
+public class TestCustomAudienceManager {
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger();
+
+    private final CustomAudienceManager mCustomAudienceManager;
+    private final String mCallerPackageName;
+
+    TestCustomAudienceManager(
+            @NonNull CustomAudienceManager customAudienceManager,
+            @NonNull String callerPackageName) {
+        Objects.requireNonNull(customAudienceManager);
+        Objects.requireNonNull(callerPackageName);
+
+        mCustomAudienceManager = customAudienceManager;
+        mCallerPackageName = callerPackageName;
+    }
+
+    /**
+     * Overrides the Custom Audience API to avoid fetching data from remote servers and use the data
+     * provided in {@link AddCustomAudienceOverrideRequest} instead. The {@link
+     * AddCustomAudienceOverrideRequest} is provided by the Ads SDK.
+     *
+     * <p>This method is intended to be used for end-to-end testing. This API is enabled only for
+     * apps in debug mode with developer options enabled.
+     *
+     * <p>This call will fail silently if the {@code owner} in the {@code request} is not the
+     * calling app's package name.
+     *
+     * @throws IllegalStateException if this API is not enabled for the caller
+     *     <p>The receiver either returns a {@code void} for a successful run, or an {@link
+     *     Exception} indicates the error.
+     */
+    @RequiresPermission(ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
+    public void overrideCustomAudienceRemoteInfo(
+            @NonNull AddCustomAudienceOverrideRequest request,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+        try {
+            final ICustomAudienceService service = mCustomAudienceManager.getService();
+            service.overrideCustomAudienceRemoteInfo(
+                    mCallerPackageName,
+                    request.getBuyer(),
+                    request.getName(),
+                    request.getBiddingLogicJs(),
+                    request.getBiddingLogicJsVersion(),
+                    request.getTrustedBiddingSignals(),
+                    new CustomAudienceOverrideCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            receiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Internal Error!", e));
+        }
+    }
+    /**
+     * Removes an override in th Custom Audience API with associated the data in {@link
+     * RemoveCustomAudienceOverrideRequest}.
+     *
+     * <p>This method is intended to be used for end-to-end testing. This API is enabled only for
+     * apps in debug mode with developer options enabled.
+     *
+     * @throws IllegalStateException if this API is not enabled for the caller
+     *     <p>The {@link RemoveCustomAudienceOverrideRequest} is provided by the Ads SDK. The
+     *     receiver either returns a {@code void} for a successful run, or an {@link Exception}
+     *     indicates the error.
+     */
+    @RequiresPermission(ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
+    public void removeCustomAudienceRemoteInfoOverride(
+            @NonNull RemoveCustomAudienceOverrideRequest request,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+        try {
+            final ICustomAudienceService service = mCustomAudienceManager.getService();
+            service.removeCustomAudienceRemoteInfoOverride(
+                    mCallerPackageName,
+                    request.getBuyer(),
+                    request.getName(),
+                    new CustomAudienceOverrideCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            receiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Internal Error!", e));
+        }
+    }
+    /**
+     * Removes all override data in the Custom Audience API.
+     *
+     * <p>This method is intended to be used for end-to-end testing. This API is enabled only for
+     * apps in debug mode with developer options enabled.
+     *
+     * @throws IllegalStateException if this API is not enabled for the caller
+     *     <p>The receiver either returns a {@code void} for a successful run, or an {@link
+     *     Exception} indicates the error.
+     */
+    @RequiresPermission(ACCESS_ADSERVICES_CUSTOM_AUDIENCE)
+    public void resetAllCustomAudienceOverrides(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+        try {
+            final ICustomAudienceService service = mCustomAudienceManager.getService();
+            service.resetAllCustomAudienceOverrides(
+                    new CustomAudienceOverrideCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            receiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Internal Error!", e));
+        }
+    }
+}
diff --git a/android-35/android/adservices/customaudience/TrustedBiddingData.java b/android-35/android/adservices/customaudience/TrustedBiddingData.java
new file mode 100644
index 0000000..0143a13
--- /dev/null
+++ b/android-35/android/adservices/customaudience/TrustedBiddingData.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.customaudience;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents data used during the ad selection process to fetch buyer bidding signals from a
+ * trusted key/value server. The fetched data is used during the ad selection process and consumed
+ * by buyer JavaScript logic running in an isolated execution environment.
+ */
+public final class TrustedBiddingData implements Parcelable {
+    @NonNull private final Uri mTrustedBiddingUri;
+    @NonNull
+    private final List<String> mTrustedBiddingKeys;
+
+    @NonNull
+    public static final Creator<TrustedBiddingData> CREATOR = new Creator<TrustedBiddingData>() {
+        @Override
+        public TrustedBiddingData createFromParcel(@NonNull Parcel in) {
+            Objects.requireNonNull(in);
+            return new TrustedBiddingData(in);
+        }
+
+        @Override
+        public TrustedBiddingData[] newArray(int size) {
+            return new TrustedBiddingData[size];
+        }
+    };
+
+    private TrustedBiddingData(@NonNull TrustedBiddingData.Builder builder) {
+        mTrustedBiddingUri = builder.mTrustedBiddingUri;
+        mTrustedBiddingKeys = builder.mTrustedBiddingKeys;
+    }
+
+    private TrustedBiddingData(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+        mTrustedBiddingUri = Uri.CREATOR.createFromParcel(in);
+        mTrustedBiddingKeys = in.createStringArrayList();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+        mTrustedBiddingUri.writeToParcel(dest, flags);
+        dest.writeStringList(mTrustedBiddingKeys);
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * @return the URI pointing to the trusted key-value server holding bidding signals. The URI
+     *     must use HTTPS.
+     */
+    @NonNull
+    public Uri getTrustedBiddingUri() {
+        return mTrustedBiddingUri;
+    }
+
+    /**
+     * @return the list of keys to query from the trusted key-value server holding bidding signals
+     */
+    @NonNull
+    public List<String> getTrustedBiddingKeys() {
+        return mTrustedBiddingKeys;
+    }
+
+    /**
+     * @return {@code true} if two {@link TrustedBiddingData} objects contain the same information
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof TrustedBiddingData)) return false;
+        TrustedBiddingData that = (TrustedBiddingData) o;
+        return mTrustedBiddingUri.equals(that.mTrustedBiddingUri)
+                && mTrustedBiddingKeys.equals(that.mTrustedBiddingKeys);
+    }
+
+    /**
+     * @return the hash of the {@link TrustedBiddingData} object's data
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mTrustedBiddingUri, mTrustedBiddingKeys);
+    }
+
+    /** Builder for {@link TrustedBiddingData} objects. */
+    public static final class Builder {
+        @Nullable private Uri mTrustedBiddingUri;
+        @Nullable private List<String> mTrustedBiddingKeys;
+
+        // TODO(b/232883403): We may need to add @NonNUll members as args.
+        public Builder() {
+        }
+
+        /**
+         * Sets the URI pointing to a trusted key-value server used to fetch bidding signals during
+         * the ad selection process. The URI must use HTTPS.
+         */
+        @NonNull
+        public Builder setTrustedBiddingUri(@NonNull Uri trustedBiddingUri) {
+            Objects.requireNonNull(trustedBiddingUri);
+            mTrustedBiddingUri = trustedBiddingUri;
+            return this;
+        }
+
+        /**
+         * Sets the list of keys to query the trusted key-value server with.
+         * <p>
+         * This list is permitted to be empty, but it must not be null.
+         */
+        @NonNull
+        public Builder setTrustedBiddingKeys(@NonNull List<String> trustedBiddingKeys) {
+            Objects.requireNonNull(trustedBiddingKeys);
+            mTrustedBiddingKeys = trustedBiddingKeys;
+            return this;
+        }
+
+        /**
+         * Builds the {@link TrustedBiddingData} object.
+         *
+         * @throws NullPointerException if any parameters are null when built
+         */
+        @NonNull
+        public TrustedBiddingData build() {
+            Objects.requireNonNull(mTrustedBiddingUri);
+            // Note that the list of keys is allowed to be empty, but not null
+            Objects.requireNonNull(mTrustedBiddingKeys);
+
+            return new TrustedBiddingData(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/exceptions/AdServicesException.java b/android-35/android/adservices/exceptions/AdServicesException.java
new file mode 100644
index 0000000..577eac7
--- /dev/null
+++ b/android-35/android/adservices/exceptions/AdServicesException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.exceptions;
+import android.annotation.Nullable;
+/**
+ * Exception thrown by AdServices.
+ */
+public class AdServicesException extends Exception {
+    public AdServicesException(@Nullable String message, @Nullable Throwable e) {
+        super(message, e);
+    }
+    public AdServicesException(@Nullable String message) {
+        super(message);
+    }
+
+    /** @hide */
+    public AdServicesException() {
+        super();
+    }
+}
\ No newline at end of file
diff --git a/android-35/android/adservices/exceptions/AdServicesNetworkException.java b/android-35/android/adservices/exceptions/AdServicesNetworkException.java
new file mode 100644
index 0000000..a6efae4
--- /dev/null
+++ b/android-35/android/adservices/exceptions/AdServicesNetworkException.java
@@ -0,0 +1,128 @@
+/*
+ * 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 android.adservices.exceptions;
+
+import static java.util.Locale.ENGLISH;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Exception thrown by the service when a failed HTTP request is the cause of a failed API call.
+ *
+ * @hide
+ */
+public class AdServicesNetworkException extends AdServicesException {
+    /**
+     * Error code indicating that the service received an <a
+     * href="https://httpwg.org/specs/rfc9110.html#status.3xx">HTTP 3xx</a> status code.
+     */
+    public static final int ERROR_REDIRECTION = 3;
+
+    /**
+     * Error code indicating that the service received an <a
+     * href="https://httpwg.org/specs/rfc9110.html#status.4xx">HTTP 4xx</a> status code.
+     */
+    public static final int ERROR_CLIENT = 4;
+
+    /**
+     * Error code indicating that the user has sent too many requests in a given amount of time and
+     * the service received an <a href="https://httpwg.org/specs/rfc6585.html#status-429">HTTP
+     * 429</a> status code.
+     */
+    public static final int ERROR_TOO_MANY_REQUESTS = 429;
+
+    /**
+     * Error code indicating that the service received an <a
+     * href="https://httpwg.org/specs/rfc9110.html#status.4xx">HTTP 5xx</a> status code.
+     */
+    public static final int ERROR_SERVER = 5;
+
+    /** Error code indicating another type of error was encountered. */
+    public static final int ERROR_OTHER = 999;
+
+    /** Error codes indicating what caused the HTTP request to fail. */
+    @IntDef(
+            prefix = {"ERROR_"},
+            value = {
+                ERROR_REDIRECTION,
+                ERROR_CLIENT,
+                ERROR_TOO_MANY_REQUESTS,
+                ERROR_SERVER,
+                ERROR_OTHER
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ErrorCode {}
+
+    /** @hide */
+    public static final String INVALID_ERROR_CODE_MESSAGE = "Valid error code must be set.";
+
+    @ErrorCode private final int mErrorCode;
+
+    /**
+     * Constructs an {@link AdServicesNetworkException} that is caused by a failed HTTP request.
+     *
+     * @param errorCode relevant {@link ErrorCode} corresponding to the failure.
+     */
+    public AdServicesNetworkException(@ErrorCode int errorCode) {
+        super();
+
+        checkErrorCode(errorCode);
+        mErrorCode = errorCode;
+    }
+
+    /**
+     * @return the {@link ErrorCode} indicating what caused the HTTP request to fail.
+     */
+    @NonNull
+    @ErrorCode
+    public int getErrorCode() {
+        return mErrorCode;
+    }
+
+    /**
+     * @return a human-readable representation of {@link AdServicesNetworkException}.
+     */
+    @Override
+    public String toString() {
+        return String.format(
+                ENGLISH,
+                "%s: {Error code: %s}",
+                this.getClass().getCanonicalName(),
+                this.getErrorCode());
+    }
+
+    private void checkErrorCode(@ErrorCode int errorCode) {
+        switch (errorCode) {
+            case ERROR_REDIRECTION:
+                // Intentional fallthrough
+            case ERROR_CLIENT:
+                // Intentional fallthrough
+            case ERROR_TOO_MANY_REQUESTS:
+                // Intentional fallthrough
+            case ERROR_SERVER:
+                // Intentional fallthrough
+            case ERROR_OTHER:
+                break;
+            default:
+                throw new IllegalArgumentException(INVALID_ERROR_CODE_MESSAGE);
+        }
+    }
+}
diff --git a/android-35/android/adservices/exceptions/RetryableAdServicesNetworkException.java b/android-35/android/adservices/exceptions/RetryableAdServicesNetworkException.java
new file mode 100644
index 0000000..b9f1278
--- /dev/null
+++ b/android-35/android/adservices/exceptions/RetryableAdServicesNetworkException.java
@@ -0,0 +1,101 @@
+/*
+ * 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 android.adservices.exceptions;
+
+import static java.util.Locale.ENGLISH;
+
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.time.Duration;
+import java.util.Objects;
+
+/**
+ * Exception thrown by the service when the HTTP failure response that caused the API to fail
+ * contains a <a href="https://httpwg.org/specs/rfc6585.html#status-429">Retry-After header</a>.
+ *
+ * @hide
+ */
+public class RetryableAdServicesNetworkException extends AdServicesNetworkException {
+    /** @hide */
+    public static final Duration UNSET_RETRY_AFTER_VALUE = Duration.ZERO;
+
+    /** @hide */
+    public static final Duration DEFAULT_RETRY_AFTER_VALUE = Duration.ofMillis(30 * 1000);
+
+    /** @hide */
+    public static final String INVALID_RETRY_AFTER_MESSAGE =
+            "Retry-after time duration must be strictly greater than zero.";
+
+    // TODO: (b/298100114) make this final again
+    private Duration mRetryAfter;
+
+    /**
+     * Constructs an {@link RetryableAdServicesNetworkException} that is caused by a failed HTTP
+     * request.
+     *
+     * @param errorCode relevant {@link ErrorCode} corresponding to the failure.
+     * @param retryAfter time {@link Duration} to back-off until next retry.
+     */
+    public RetryableAdServicesNetworkException(
+            @ErrorCode int errorCode, @NonNull Duration retryAfter) {
+        super(errorCode);
+
+        Objects.requireNonNull(retryAfter);
+        Preconditions.checkArgument(
+                retryAfter.compareTo(UNSET_RETRY_AFTER_VALUE) > 0, INVALID_RETRY_AFTER_MESSAGE);
+
+        mRetryAfter = retryAfter;
+    }
+
+    /**
+     * If {@link #mRetryAfter} < {@code defaultDuration}, it gets set to {@code defaultDuration}. If
+     * {@link #mRetryAfter} > {@code maxDuration}, it gets set to {@code maxDuration}. If it falls
+     * in the range of both numbers, it stays the same.
+     *
+     * @hide
+     */
+    public void setRetryAfterToValidDuration(long defaultDuration, long maxDuration) {
+        // TODO: (b/298100114) this is a hack! this method should be removed after resolving the bug
+        mRetryAfter =
+                Duration.ofMillis(
+                        Math.min(Math.max(mRetryAfter.toMillis(), defaultDuration), maxDuration));
+    }
+
+    /**
+     * @return the positive retry-after {@link Duration} if set or else {@link Duration#ZERO} by
+     *     default.
+     */
+    @NonNull
+    public Duration getRetryAfter() {
+        return mRetryAfter;
+    }
+
+    /**
+     * @return a human-readable representation of {@link RetryableAdServicesNetworkException}.
+     */
+    @Override
+    public String toString() {
+        return String.format(
+                ENGLISH,
+                "%s: {Error code: %s, Retry after: %sms}",
+                this.getClass().getCanonicalName(),
+                this.getErrorCode(),
+                this.getRetryAfter().toMillis());
+    }
+}
diff --git a/android-35/android/adservices/exceptions/UnsupportedPayloadSizeException.java b/android-35/android/adservices/exceptions/UnsupportedPayloadSizeException.java
new file mode 100644
index 0000000..a67428e
--- /dev/null
+++ b/android-35/android/adservices/exceptions/UnsupportedPayloadSizeException.java
@@ -0,0 +1,39 @@
+/*
+ * 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 android.adservices.exceptions;
+
+import android.annotation.Nullable;
+
+/**
+ * Exception used when the size of a payload is not supported.
+ *
+ * @hide
+ */
+public class UnsupportedPayloadSizeException extends IllegalStateException {
+    private final int mPayloadSizeKb;
+
+    /** Constructs a {@link UnsupportedPayloadSizeException} */
+    public UnsupportedPayloadSizeException(int payloadSizeKb, @Nullable String message) {
+        super(message);
+        this.mPayloadSizeKb = payloadSizeKb;
+    }
+
+    /** Returns the size of the payload in Kb */
+    public int getPayloadSizeKb() {
+        return mPayloadSizeKb;
+    }
+}
diff --git a/android-35/android/adservices/extdata/AdServicesExtDataParams.java b/android-35/android/adservices/extdata/AdServicesExtDataParams.java
new file mode 100644
index 0000000..fab883a
--- /dev/null
+++ b/android-35/android/adservices/extdata/AdServicesExtDataParams.java
@@ -0,0 +1,311 @@
+/*
+ * 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 android.adservices.extdata;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Container for the data fields handled by {@link AdServicesExtDataStorageService}.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_ADEXT_DATA_SERVICE_APIS_ENABLED)
+public final class AdServicesExtDataParams implements Parcelable {
+    /**
+     * Custom tri-state boolean type to represent true, false, and unknown
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            prefix = "BOOLEAN_",
+            value = {BOOLEAN_TRUE, BOOLEAN_FALSE, BOOLEAN_UNKNOWN})
+    public @interface TriStateBoolean {}
+
+    /**
+     * Int value to represent true.
+     *
+     * @hide
+     */
+    public static final int BOOLEAN_TRUE = 1;
+
+    /**
+     * Int value to represent false.
+     *
+     * @hide
+     */
+    public static final int BOOLEAN_FALSE = 0;
+
+    /**
+     * Int value to represent unknown.
+     *
+     * @hide
+     */
+    public static final int BOOLEAN_UNKNOWN = -1;
+
+    /**
+     * Type to represent user manual interaction state.
+     *
+     * @hide
+     */
+    @IntDef(
+            prefix = "STATE_",
+            value = {
+                STATE_NO_MANUAL_INTERACTIONS_RECORDED,
+                STATE_UNKNOWN,
+                STATE_MANUAL_INTERACTIONS_RECORDED
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UserManualInteraction {}
+
+    /**
+     * Int value to represent no manual interaction recorded state.
+     *
+     * @hide
+     */
+    public static final int STATE_NO_MANUAL_INTERACTIONS_RECORDED = -1;
+
+    /**
+     * Int value to represent unknown manual interaction state.
+     *
+     * @hide
+     */
+    public static final int STATE_UNKNOWN = 0;
+
+    /**
+     * Int value to represent manual interaction reported state.
+     *
+     * @hide
+     */
+    public static final int STATE_MANUAL_INTERACTIONS_RECORDED = 1;
+
+    @TriStateBoolean private final int mIsNotificationDisplayed;
+    @TriStateBoolean private final int mIsMeasurementConsented;
+    @TriStateBoolean private final int mIsU18Account;
+    @TriStateBoolean private final int mIsAdultAccount;
+    @UserManualInteraction private final int mManualInteractionWithConsentStatus;
+    private final long mMeasurementRollbackApexVersion;
+
+    /**
+     * Init AdServicesExtDataParams.
+     *
+     * @param isNotificationDisplayed 1 if notification is displayed, 0 if notification not
+     *     displayed, -1 to represent no data.
+     * @param isMeasurementConsented 1 if measurement consented, 0 if not, -1 to represent no data.
+     * @param isU18Account 1 if account is U18, 0 if not, -1 if no data.
+     * @param isAdultAccount 1 if adult account, 0 if not, -1 if no data.
+     * @param manualInteractionWithConsentStatus 1 if user interacted, -1 if not, 0 if unknown.
+     * @param measurementRollbackApexVersion ExtServices apex version for measurement rollback
+     *     handling. -1 if no data.
+     */
+    public AdServicesExtDataParams(
+            @TriStateBoolean int isNotificationDisplayed,
+            @TriStateBoolean int isMeasurementConsented,
+            @TriStateBoolean int isU18Account,
+            @TriStateBoolean int isAdultAccount,
+            @UserManualInteraction int manualInteractionWithConsentStatus,
+            long measurementRollbackApexVersion) {
+        mIsNotificationDisplayed = isNotificationDisplayed;
+        mIsMeasurementConsented = isMeasurementConsented;
+        mIsU18Account = isU18Account;
+        mIsAdultAccount = isAdultAccount;
+        mManualInteractionWithConsentStatus = manualInteractionWithConsentStatus;
+        mMeasurementRollbackApexVersion = measurementRollbackApexVersion;
+    }
+
+    private AdServicesExtDataParams(@NonNull Parcel in) {
+        mIsNotificationDisplayed = in.readInt();
+        mIsMeasurementConsented = in.readInt();
+        mIsU18Account = in.readInt();
+        mIsAdultAccount = in.readInt();
+        mManualInteractionWithConsentStatus = in.readInt();
+        mMeasurementRollbackApexVersion = in.readLong();
+    }
+
+    /** Creator for Parcelable. */
+    @NonNull
+    public static final Creator<AdServicesExtDataParams> CREATOR =
+            new Creator<AdServicesExtDataParams>() {
+                @Override
+                public AdServicesExtDataParams createFromParcel(Parcel in) {
+                    return new AdServicesExtDataParams(in);
+                }
+
+                @Override
+                public AdServicesExtDataParams[] newArray(int size) {
+                    return new AdServicesExtDataParams[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mIsNotificationDisplayed);
+        out.writeInt(mIsMeasurementConsented);
+        out.writeInt(mIsU18Account);
+        out.writeInt(mIsAdultAccount);
+        out.writeInt(mManualInteractionWithConsentStatus);
+        out.writeLong(mMeasurementRollbackApexVersion);
+    }
+
+    /** Returns 1 if notification was shown on R, 0 if not shown, -1 if unknown. */
+    @TriStateBoolean
+    public int getIsNotificationDisplayed() {
+        return mIsNotificationDisplayed;
+    }
+
+    /** Returns 1 if measurement was consented, 0 if not, -1 if unknown. */
+    @TriStateBoolean
+    public int getIsMeasurementConsented() {
+        return mIsMeasurementConsented;
+    }
+
+    /** Returns 1 if account is U18 account, 0 if not, -1 if unknown. */
+    @TriStateBoolean
+    public int getIsU18Account() {
+        return mIsU18Account;
+    }
+
+    /** Returns 1 if account is adult account, 0 if not, -1 if unknown. */
+    @TriStateBoolean
+    public int getIsAdultAccount() {
+        return mIsAdultAccount;
+    }
+
+    /** Returns 1 if user interacted, -1 if not, 0 if unknown. */
+    @UserManualInteraction
+    public int getManualInteractionWithConsentStatus() {
+        return mManualInteractionWithConsentStatus;
+    }
+
+    /**
+     * Returns ExtServices apex version for handling measurement rollback. -1 is returned if no data
+     * is available.
+     */
+    public long getMeasurementRollbackApexVersion() {
+        return mMeasurementRollbackApexVersion;
+    }
+
+    @SuppressLint("DefaultLocale")
+    @Override
+    public String toString() {
+        return String.format(
+                "AdServicesExtDataParams{"
+                        + "mIsNotificationDisplayed=%d, "
+                        + "mIsMsmtConsented=%d, "
+                        + "mIsU18Account=%d, "
+                        + "mIsAdultAccount=%d, "
+                        + "mManualInteractionWithConsentStatus=%d, "
+                        + "mMsmtRollbackApexVersion=%d}",
+                mIsNotificationDisplayed,
+                mIsMeasurementConsented,
+                mIsU18Account,
+                mIsAdultAccount,
+                mManualInteractionWithConsentStatus,
+                mMeasurementRollbackApexVersion);
+    }
+
+    /**
+     * Builder for {@link AdServicesExtDataParams} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        @TriStateBoolean private int mNotificationDisplayed;
+        @TriStateBoolean private int mMsmtConsent;
+        @TriStateBoolean private int mIsU18Account;
+        @TriStateBoolean private int mIsAdultAccount;
+        @UserManualInteraction private int mManualInteractionWithConsentStatus;
+        private long mMsmtRollbackApexVersion;
+
+        /** Set the isNotificationDisplayed. */
+        @NonNull
+        public AdServicesExtDataParams.Builder setNotificationDisplayed(
+                @TriStateBoolean int notificationDisplayed) {
+            mNotificationDisplayed = notificationDisplayed;
+            return this;
+        }
+
+        /** Set the isMeasurementConsented. */
+        @NonNull
+        public AdServicesExtDataParams.Builder setMsmtConsent(@TriStateBoolean int msmtConsent) {
+            mMsmtConsent = msmtConsent;
+            return this;
+        }
+
+        /** Set the isU18Account. */
+        @NonNull
+        public AdServicesExtDataParams.Builder setIsU18Account(@TriStateBoolean int isU18Account) {
+            mIsU18Account = isU18Account;
+            return this;
+        }
+
+        /** Set the isAdultAccount. */
+        @NonNull
+        public AdServicesExtDataParams.Builder setIsAdultAccount(
+                @TriStateBoolean int isAdultAccount) {
+            mIsAdultAccount = isAdultAccount;
+            return this;
+        }
+
+        /** Set the manualInteractionWithConsentStatus. */
+        @NonNull
+        public AdServicesExtDataParams.Builder setManualInteractionWithConsentStatus(
+                @UserManualInteraction int manualInteractionWithConsentStatus) {
+            mManualInteractionWithConsentStatus = manualInteractionWithConsentStatus;
+            return this;
+        }
+
+        /** Set the msmtRollbackApexVersion. */
+        @NonNull
+        public AdServicesExtDataParams.Builder setMsmtRollbackApexVersion(
+                long msmtRollbackApexVersion) {
+            mMsmtRollbackApexVersion = msmtRollbackApexVersion;
+            return this;
+        }
+
+        public Builder() {}
+
+        /** Builds a {@link AdServicesExtDataParams} instance. */
+        @NonNull
+        public AdServicesExtDataParams build() {
+            return new AdServicesExtDataParams(
+                    mNotificationDisplayed,
+                    mMsmtConsent,
+                    mIsU18Account,
+                    mIsAdultAccount,
+                    mManualInteractionWithConsentStatus,
+                    mMsmtRollbackApexVersion);
+        }
+    }
+}
diff --git a/android-35/android/adservices/extdata/AdServicesExtDataStorageService.java b/android-35/android/adservices/extdata/AdServicesExtDataStorageService.java
new file mode 100644
index 0000000..cd2644e
--- /dev/null
+++ b/android-35/android/adservices/extdata/AdServicesExtDataStorageService.java
@@ -0,0 +1,153 @@
+/*
+ * 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 android.adservices.extdata;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.adservices.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Abstract base class to implement AdServicesExtDataStorageService.
+ *
+ * <p>The implementor of this service needs to override the onGetAdServicesExtData and
+ * onPutAdServicesExtData methods
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_ADEXT_DATA_SERVICE_APIS_ENABLED)
+public abstract class AdServicesExtDataStorageService extends Service {
+    /**
+     * Supported data field IDs.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            prefix = "FIELD_",
+            value = {
+                FIELD_IS_NOTIFICATION_DISPLAYED,
+                FIELD_IS_MEASUREMENT_CONSENTED,
+                FIELD_IS_U18_ACCOUNT,
+                FIELD_IS_ADULT_ACCOUNT,
+                FIELD_MANUAL_INTERACTION_WITH_CONSENT_STATUS,
+                FIELD_MEASUREMENT_ROLLBACK_APEX_VERSION,
+            })
+    public @interface AdServicesExtDataFieldId {}
+
+    /** Field to represent whether AdServices consent notification has been shown on Android R. */
+    public static final int FIELD_IS_NOTIFICATION_DISPLAYED = 0;
+
+    /** Field to represent whether user provided consent for Measurement API. */
+    public static final int FIELD_IS_MEASUREMENT_CONSENTED = 1;
+
+    /** Field to represent whether account is U18. */
+    public static final int FIELD_IS_U18_ACCOUNT = 2;
+
+    /** Field to represent whether it's an adult account. */
+    public static final int FIELD_IS_ADULT_ACCOUNT = 3;
+
+    /** Field to represent whether user manually interacted with consent */
+    public static final int FIELD_MANUAL_INTERACTION_WITH_CONSENT_STATUS = 4;
+
+    /** Field to represent ExtServices apex version for measurement rollback handling. */
+    public static final int FIELD_MEASUREMENT_ROLLBACK_APEX_VERSION = 5;
+
+    /** The intent that the service must respond to. Add it to the intent filter of the service. */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.adservices.extdata.AdServicesExtDataStorageService";
+
+    public AdServicesExtDataStorageService() {}
+
+    @Nullable
+    @Override
+    public final IBinder onBind(@Nullable Intent intent) {
+        return mInterface.asBinder();
+    }
+
+    /** Abstract onGetAdServicesExtData method to get all stored ext data values from data store. */
+    @NonNull
+    public abstract AdServicesExtDataParams onGetAdServicesExtData();
+
+    /**
+     * Abstract onPutAdServicesExtData method to update values of fields in data store.
+     *
+     * @param adServicesExtDataParams data object that stores fields to be updated.
+     * @param adServicesExtDataFields explicit list of fields that need to be updated in data store.
+     */
+    public abstract void onPutAdServicesExtData(
+            @NonNull AdServicesExtDataParams adServicesExtDataParams,
+            @NonNull @AdServicesExtDataFieldId int[] adServicesExtDataFields);
+
+    private final IAdServicesExtDataStorageService mInterface =
+            new IAdServicesExtDataStorageService.Stub() {
+
+                @Override
+                public void getAdServicesExtData(@NonNull IGetAdServicesExtDataCallback callback)
+                        throws RemoteException {
+                    Objects.requireNonNull(callback);
+
+                    try {
+                        AdServicesExtDataParams adServicesExtDataParams = onGetAdServicesExtData();
+
+                        GetAdServicesExtDataResult result =
+                                new GetAdServicesExtDataResult.Builder()
+                                        .setAdServicesExtDataParams(adServicesExtDataParams)
+                                        .build();
+                        callback.onResult(result);
+                    } catch (Exception e) {
+                        callback.onError(e.getMessage());
+                    }
+                }
+
+                @Override
+                public void putAdServicesExtData(
+                        @NonNull AdServicesExtDataParams params,
+                        @NonNull @AdServicesExtDataFieldId int[] adServicesExtDataFields,
+                        @NonNull IGetAdServicesExtDataCallback callback)
+                        throws RemoteException {
+                    Objects.requireNonNull(params);
+                    Objects.requireNonNull(adServicesExtDataFields);
+                    Objects.requireNonNull(callback);
+
+                    try {
+                        onPutAdServicesExtData(params, adServicesExtDataFields);
+                        GetAdServicesExtDataResult result =
+                                new GetAdServicesExtDataResult.Builder()
+                                        .setAdServicesExtDataParams(params)
+                                        .build();
+                        callback.onResult(result);
+                    } catch (Exception e) {
+                        callback.onError(e.getMessage());
+                    }
+                }
+            };
+}
diff --git a/android-35/android/adservices/extdata/GetAdServicesExtDataResult.java b/android-35/android/adservices/extdata/GetAdServicesExtDataResult.java
new file mode 100644
index 0000000..176d0d2
--- /dev/null
+++ b/android-35/android/adservices/extdata/GetAdServicesExtDataResult.java
@@ -0,0 +1,156 @@
+/*
+ * 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 android.adservices.extdata;
+
+import android.adservices.common.AdServicesResponse;
+import android.adservices.common.AdServicesStatusUtils;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.os.Parcel;
+
+import java.util.Objects;
+
+/**
+ * Represent the result from {@link AdServicesExtDataStorageService} API.
+ *
+ * @hide
+ */
+public final class GetAdServicesExtDataResult extends AdServicesResponse {
+    @NonNull private final AdServicesExtDataParams mAdServicesExtDataParams;
+
+    private GetAdServicesExtDataResult(
+            @AdServicesStatusUtils.StatusCode int resultCode,
+            @Nullable String errorMessage,
+            @NonNull AdServicesExtDataParams adServicesExtDataParams) {
+        super(resultCode, errorMessage);
+        mAdServicesExtDataParams = Objects.requireNonNull(adServicesExtDataParams);
+    }
+
+    private GetAdServicesExtDataResult(@NonNull Parcel in) {
+        super(in);
+        Objects.requireNonNull(in);
+        // Method deprecated starting from Android T; however, AdServicesExtDataStorageService is
+        // intended to only be used on Android S-.
+        mAdServicesExtDataParams =
+                in.readParcelable(AdServicesExtDataParams.class.getClassLoader());
+    }
+
+    /** Creator for Parcelable. */
+    @NonNull
+    public static final Creator<GetAdServicesExtDataResult> CREATOR =
+            new Creator<GetAdServicesExtDataResult>() {
+                @Override
+                public GetAdServicesExtDataResult createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new GetAdServicesExtDataResult(in);
+                }
+
+                @Override
+                public GetAdServicesExtDataResult[] newArray(int size) {
+                    return new GetAdServicesExtDataResult[size];
+                }
+            };
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mStatusCode);
+        out.writeString(mErrorMessage);
+        out.writeParcelable(mAdServicesExtDataParams, flags);
+    }
+
+    /**
+     * Returns the error message associated with this result.
+     *
+     * <p>If {@link #isSuccess} is {@code true}, the error message is always {@code null}. The error
+     * message may be {@code null} even if {@link #isSuccess} is {@code false}.
+     */
+    @Nullable
+    public String getErrorMessage() {
+        return mErrorMessage;
+    }
+
+    /** Returns the AdServicesExtDataParams value. */
+    @NonNull
+    public AdServicesExtDataParams getAdServicesExtDataParams() {
+        return mAdServicesExtDataParams;
+    }
+
+    @SuppressLint("DefaultLocale")
+    @Override
+    public String toString() {
+        return String.format(
+                "GetAdServicesExtIntDataResult{"
+                        + "mResultCode=%d, "
+                        + "mErrorMessage=%s, "
+                        + "mAdServicesExtDataParams=%s}",
+                mStatusCode, mErrorMessage, mAdServicesExtDataParams.toString());
+    }
+
+    /**
+     * Builder for {@link GetAdServicesExtDataResult} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        @AdServicesStatusUtils.StatusCode private int mStatusCode;
+
+        @Nullable private String mErrorMessage;
+        @NonNull private AdServicesExtDataParams mAdServicesExtDataParams;
+
+        public Builder() {}
+
+        /** Set the Result Code. */
+        @NonNull
+        public Builder setStatusCode(@AdServicesStatusUtils.StatusCode int statusCode) {
+            mStatusCode = statusCode;
+            return this;
+        }
+
+        /** Set the Error Message. */
+        @NonNull
+        public Builder setErrorMessage(@Nullable String errorMessage) {
+            mErrorMessage = errorMessage;
+            return this;
+        }
+
+        /** Set the AdServicesExtDataParams */
+        @NonNull
+        public Builder setAdServicesExtDataParams(AdServicesExtDataParams adServicesExtDataParams) {
+            mAdServicesExtDataParams = adServicesExtDataParams;
+            return this;
+        }
+
+        /** Builds a {@link GetAdServicesExtDataResult} instance. */
+        @NonNull
+        public GetAdServicesExtDataResult build() {
+            if (mAdServicesExtDataParams == null) {
+                throw new IllegalArgumentException("AdServicesExtDataParams is null");
+            }
+
+            return new GetAdServicesExtDataResult(
+                    mStatusCode, mErrorMessage, mAdServicesExtDataParams);
+        }
+    }
+}
diff --git a/android-35/android/adservices/measurement/DeletionParam.java b/android-35/android/adservices/measurement/DeletionParam.java
new file mode 100644
index 0000000..00d5fad
--- /dev/null
+++ b/android-35/android/adservices/measurement/DeletionParam.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.measurement;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Class to hold deletion related request. This is an internal class for communication between the
+ * {@link MeasurementManager} and {@link IMeasurementService} impl.
+ *
+ * @hide
+ */
+public final class DeletionParam implements Parcelable {
+    private final List<Uri> mOriginUris;
+    private final List<Uri> mDomainUris;
+    private final Instant mStart;
+    private final Instant mEnd;
+    private final String mAppPackageName;
+    private final String mSdkPackageName;
+    @DeletionRequest.DeletionMode private final int mDeletionMode;
+    @DeletionRequest.MatchBehavior private final int mMatchBehavior;
+
+    private DeletionParam(@NonNull Builder builder) {
+        mOriginUris = builder.mOriginUris;
+        mDomainUris = builder.mDomainUris;
+        mDeletionMode = builder.mDeletionMode;
+        mMatchBehavior = builder.mMatchBehavior;
+        mStart = builder.mStart;
+        mEnd = builder.mEnd;
+        mAppPackageName = builder.mAppPackageName;
+        mSdkPackageName = builder.mSdkPackageName;
+    }
+
+    /** Unpack an DeletionRequest from a Parcel. */
+    private DeletionParam(Parcel in) {
+        mAppPackageName = in.readString();
+        mSdkPackageName = in.readString();
+
+        mDomainUris = new ArrayList<>();
+        in.readTypedList(mDomainUris, Uri.CREATOR);
+
+        mOriginUris = new ArrayList<>();
+        in.readTypedList(mOriginUris, Uri.CREATOR);
+
+        boolean hasStart = in.readBoolean();
+        if (hasStart) {
+            mStart = Instant.parse(in.readString());
+        } else {
+            mStart = null;
+        }
+
+        boolean hasEnd = in.readBoolean();
+        if (hasEnd) {
+            mEnd = Instant.parse(in.readString());
+        } else {
+            mEnd = null;
+        }
+
+        mDeletionMode = in.readInt();
+        mMatchBehavior = in.readInt();
+    }
+
+    /** Creator for Parcelable (via reflection). */
+    @NonNull
+    public static final Parcelable.Creator<DeletionParam> CREATOR =
+            new Parcelable.Creator<DeletionParam>() {
+                @Override
+                public DeletionParam createFromParcel(Parcel in) {
+                    return new DeletionParam(in);
+                }
+
+                @Override
+                public DeletionParam[] newArray(int size) {
+                    return new DeletionParam[size];
+                }
+            };
+
+    /** For Parcelable, no special marshalled objects. */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** For Parcelable, write out to a Parcel in particular order. */
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        Objects.requireNonNull(out);
+        out.writeString(mAppPackageName);
+        out.writeString(mSdkPackageName);
+
+        out.writeTypedList(mDomainUris);
+
+        out.writeTypedList(mOriginUris);
+
+        if (mStart != null) {
+            out.writeBoolean(true);
+            out.writeString(mStart.toString());
+        } else {
+            out.writeBoolean(false);
+        }
+
+        if (mEnd != null) {
+            out.writeBoolean(true);
+            out.writeString(mEnd.toString());
+        } else {
+            out.writeBoolean(false);
+        }
+
+        out.writeInt(mDeletionMode);
+
+        out.writeInt(mMatchBehavior);
+    }
+
+    /**
+     * Publisher/Advertiser Origins for which data should be deleted. These will be matched as-is.
+     */
+    @NonNull
+    public List<Uri> getOriginUris() {
+        return mOriginUris;
+    }
+
+    /**
+     * Publisher/Advertiser domains for which data should be deleted. These will be pattern matched
+     * with regex SCHEME://(.*\.|)SITE .
+     */
+    @NonNull
+    public List<Uri> getDomainUris() {
+        return mDomainUris;
+    }
+
+    /** Deletion mode for matched records. */
+    @DeletionRequest.DeletionMode
+    public int getDeletionMode() {
+        return mDeletionMode;
+    }
+
+    /** Match behavior for provided origins/domains. */
+    @DeletionRequest.MatchBehavior
+    public int getMatchBehavior() {
+        return mMatchBehavior;
+    }
+
+    /**
+     * Instant in time the deletion starts, or {@link java.time.Instant#MIN} if starting at the
+     * oldest possible time.
+     */
+    @NonNull
+    public Instant getStart() {
+        return mStart;
+    }
+
+    /**
+     * Instant in time the deletion ends, or {@link java.time.Instant#MAX} if ending at the most
+     * recent time.
+     */
+    @NonNull
+    public Instant getEnd() {
+        return mEnd;
+    }
+
+    /** Package name of the app used for the deletion. */
+    @NonNull
+    public String getAppPackageName() {
+        return mAppPackageName;
+    }
+
+    /** Package name of the sdk used for the deletion. */
+    @NonNull
+    public String getSdkPackageName() {
+        return mSdkPackageName;
+    }
+
+    /** A builder for {@link DeletionParam}. */
+    public static final class Builder {
+        private final List<Uri> mOriginUris;
+        private final List<Uri> mDomainUris;
+        private final Instant mStart;
+        private final Instant mEnd;
+        private final String mAppPackageName;
+        private final String mSdkPackageName;
+        @DeletionRequest.DeletionMode private int mDeletionMode;
+        @DeletionRequest.MatchBehavior private int mMatchBehavior;
+
+        /**
+         * Builder constructor for {@link DeletionParam}.
+         *
+         * @param originUris see {@link DeletionParam#getOriginUris()}
+         * @param domainUris see {@link DeletionParam#getDomainUris()}
+         * @param start see {@link DeletionParam#getStart()}
+         * @param end see {@link DeletionParam#getEnd()}
+         * @param appPackageName see {@link DeletionParam#getAppPackageName()}
+         * @param sdkPackageName see {@link DeletionParam#getSdkPackageName()}
+         */
+        public Builder(
+                @NonNull List<Uri> originUris,
+                @NonNull List<Uri> domainUris,
+                @NonNull Instant start,
+                @NonNull Instant end,
+                @NonNull String appPackageName,
+                @NonNull String sdkPackageName) {
+            Objects.requireNonNull(originUris);
+            Objects.requireNonNull(domainUris);
+            Objects.requireNonNull(start);
+            Objects.requireNonNull(end);
+            Objects.requireNonNull(appPackageName);
+            Objects.requireNonNull(sdkPackageName);
+
+            mOriginUris = originUris;
+            mDomainUris = domainUris;
+            mStart = start;
+            mEnd = end;
+            mAppPackageName = appPackageName;
+            mSdkPackageName = sdkPackageName;
+        }
+
+        /** See {@link DeletionParam#getDeletionMode()}. */
+        @NonNull
+        public Builder setDeletionMode(@DeletionRequest.DeletionMode int deletionMode) {
+            mDeletionMode = deletionMode;
+            return this;
+        }
+
+        /** See {@link DeletionParam#getDeletionMode()}. */
+        @NonNull
+        public Builder setMatchBehavior(@DeletionRequest.MatchBehavior int matchBehavior) {
+            mMatchBehavior = matchBehavior;
+            return this;
+        }
+
+        /** Build the DeletionRequest. */
+        @NonNull
+        public DeletionParam build() {
+            return new DeletionParam(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/measurement/DeletionRequest.java b/android-35/android/adservices/measurement/DeletionRequest.java
new file mode 100644
index 0000000..0fc5f4f
--- /dev/null
+++ b/android-35/android/adservices/measurement/DeletionRequest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.measurement;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/** Deletion Request. */
+public class DeletionRequest {
+
+    /**
+     * Deletion modes for matched records.
+     *
+     * @hide
+     */
+    @IntDef(value = {DELETION_MODE_ALL, DELETION_MODE_EXCLUDE_INTERNAL_DATA})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DeletionMode {}
+
+    /**
+     * Matching Behaviors for params.
+     *
+     * @hide
+     */
+    @IntDef(value = {MATCH_BEHAVIOR_DELETE, MATCH_BEHAVIOR_PRESERVE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MatchBehavior {}
+
+    /** Deletion mode to delete all data associated with the selected records. */
+    public static final int DELETION_MODE_ALL = 0;
+
+    /**
+     * Deletion mode to delete all data except the internal data (e.g. rate limits) for the selected
+     * records.
+     */
+    public static final int DELETION_MODE_EXCLUDE_INTERNAL_DATA = 1;
+
+    /** Match behavior option to delete the supplied params (Origin/Domains). */
+    public static final int MATCH_BEHAVIOR_DELETE = 0;
+
+    /**
+     * Match behavior option to preserve the supplied params (Origin/Domains) and delete everything
+     * else.
+     */
+    public static final int MATCH_BEHAVIOR_PRESERVE = 1;
+
+    private final Instant mStart;
+    private final Instant mEnd;
+    private final List<Uri> mOriginUris;
+    private final List<Uri> mDomainUris;
+    private final @MatchBehavior int mMatchBehavior;
+    private final @DeletionMode int mDeletionMode;
+
+    private DeletionRequest(@NonNull Builder builder) {
+        mOriginUris = builder.mOriginUris;
+        mDomainUris = builder.mDomainUris;
+        mMatchBehavior = builder.mMatchBehavior;
+        mDeletionMode = builder.mDeletionMode;
+        mStart = builder.mStart;
+        mEnd = builder.mEnd;
+    }
+
+    /** Get the list of origin URIs. */
+    @NonNull
+    public List<Uri> getOriginUris() {
+        return mOriginUris;
+    }
+
+    /** Get the list of domain URIs. */
+    @NonNull
+    public List<Uri> getDomainUris() {
+        return mDomainUris;
+    }
+
+    /** Get the deletion mode. */
+    public @DeletionMode int getDeletionMode() {
+        return mDeletionMode;
+    }
+
+    /** Get the match behavior. */
+    public @MatchBehavior int getMatchBehavior() {
+        return mMatchBehavior;
+    }
+
+    /** Get the start of the deletion range. */
+    @NonNull
+    public Instant getStart() {
+        return mStart;
+    }
+
+    /** Get the end of the deletion range. */
+    @NonNull
+    public Instant getEnd() {
+        return mEnd;
+    }
+
+    /** Builder for {@link DeletionRequest} objects. */
+    public static final class Builder {
+        private Instant mStart = Instant.MIN;
+        private Instant mEnd = Instant.MAX;
+        private List<Uri> mOriginUris;
+        private List<Uri> mDomainUris;
+        @MatchBehavior private int mMatchBehavior;
+        @DeletionMode private int mDeletionMode;
+
+        public Builder() {}
+
+        /**
+         * Set the list of origin URI which will be used for matching. These will be matched with
+         * records using the same origin only, i.e. subdomains won't match. E.g. If originUri is
+         * {@code https://a.example.com}, then {@code https://a.example.com} will match; {@code
+         * https://example.com}, {@code https://b.example.com} and {@code https://abcexample.com}
+         * will NOT match.
+         */
+        public @NonNull Builder setOriginUris(@Nullable List<Uri> originUris) {
+            mOriginUris = originUris;
+            return this;
+        }
+
+        /**
+         * Set the list of domain URI which will be used for matching. These will be matched with
+         * records using the same domain or any subdomains. E.g. If domainUri is {@code
+         * https://example.com}, then {@code https://a.example.com}, {@code https://example.com} and
+         * {@code https://b.example.com} will match; {@code https://abcexample.com} will NOT match.
+         */
+        public @NonNull Builder setDomainUris(@Nullable List<Uri> domainUris) {
+            mDomainUris = domainUris;
+            return this;
+        }
+
+        /**
+         * Set the match behavior for the supplied params. {@link #MATCH_BEHAVIOR_DELETE}: This
+         * option will use the supplied params (Origin URIs & Domain URIs) for selecting records for
+         * deletion. {@link #MATCH_BEHAVIOR_PRESERVE}: This option will preserve the data associated
+         * with the supplied params (Origin URIs & Domain URIs) and select remaining records for
+         * deletion.
+         */
+        public @NonNull Builder setMatchBehavior(@MatchBehavior int matchBehavior) {
+            mMatchBehavior = matchBehavior;
+            return this;
+        }
+
+        /**
+         * Set the match behavior for the supplied params. {@link #DELETION_MODE_ALL}: All data
+         * associated with the selected records will be deleted. {@link
+         * #DELETION_MODE_EXCLUDE_INTERNAL_DATA}: All data except the internal system data (e.g.
+         * rate limits) associated with the selected records will be deleted.
+         */
+        public @NonNull Builder setDeletionMode(@DeletionMode int deletionMode) {
+            mDeletionMode = deletionMode;
+            return this;
+        }
+
+        /**
+         * Set the start of the deletion range. Passing in {@link java.time.Instant#MIN} will cause
+         * everything from the oldest record to the specified end be deleted. No set start will
+         * default to {@link java.time.Instant#MIN}.
+         */
+        public @NonNull Builder setStart(@NonNull Instant start) {
+            Objects.requireNonNull(start);
+            mStart = start;
+            return this;
+        }
+
+        /**
+         * Set the end of the deletion range. Passing in {@link java.time.Instant#MAX} will cause
+         * everything from the specified start until the newest record to be deleted. No set end
+         * will default to {@link java.time.Instant#MAX}.
+         */
+        public @NonNull Builder setEnd(@NonNull Instant end) {
+            Objects.requireNonNull(end);
+            mEnd = end;
+            return this;
+        }
+
+        /** Builds a {@link DeletionRequest} instance. */
+        public @NonNull DeletionRequest build() {
+            if (mDomainUris == null) {
+                mDomainUris = new ArrayList<>();
+            }
+            if (mOriginUris == null) {
+                mOriginUris = new ArrayList<>();
+            }
+            return new DeletionRequest(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/measurement/MeasurementCompatibleManager.java b/android-35/android/adservices/measurement/MeasurementCompatibleManager.java
new file mode 100644
index 0000000..58ac44e
--- /dev/null
+++ b/android-35/android/adservices/measurement/MeasurementCompatibleManager.java
@@ -0,0 +1,742 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.measurement;
+
+import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION;
+
+import android.adservices.adid.AdId;
+import android.adservices.adid.AdIdCompatibleManager;
+import android.adservices.common.AdServicesOutcomeReceiver;
+import android.adservices.common.AdServicesStatusUtils;
+import android.adservices.common.CallerMetadata;
+import android.adservices.common.SandboxedSdkContextUtils;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.app.sdksandbox.SandboxedSdkContext;
+import android.content.Context;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.view.InputEvent;
+
+import com.android.adservices.AdServicesCommon;
+import com.android.adservices.LogUtil;
+import com.android.adservices.ServiceBinder;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * MeasurementManager provides APIs to manage source and trigger registrations.
+ *
+ * @hide
+ */
+public class MeasurementCompatibleManager {
+    private interface MeasurementAdIdCallback {
+        void onAdIdCallback(boolean isAdIdEnabled, @Nullable String adIdValue);
+    }
+
+    private static final long AD_ID_TIMEOUT_MS = 400;
+
+    private final Context mContext;
+    private final ServiceBinder<IMeasurementService> mServiceBinder;
+    private AdIdCompatibleManager mAdIdManager;
+    private final Executor mAdIdExecutor = Executors.newCachedThreadPool();
+
+    private static final String DEBUG_API_WARNING_MESSAGE =
+            "To enable debug api, include ACCESS_ADSERVICES_AD_ID "
+                    + "permission and enable advertising ID under device settings";
+
+    /**
+     * This is for test purposes, it helps to mock the adIdManager.
+     *
+     * @hide
+     */
+    @NonNull
+    public static MeasurementCompatibleManager get(@NonNull Context context) {
+        return new MeasurementCompatibleManager(context);
+    }
+
+    /**
+     * This is for test purposes, it helps to mock the adIdManager.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    @NonNull
+    public static MeasurementCompatibleManager get(
+            @NonNull Context context, @NonNull AdIdCompatibleManager adIdManager) {
+        MeasurementCompatibleManager measurementManager = MeasurementCompatibleManager.get(context);
+        measurementManager.mAdIdManager = adIdManager;
+        return measurementManager;
+    }
+
+    /**
+     * Create MeasurementCompatibleManager.
+     *
+     * @hide
+     */
+    private MeasurementCompatibleManager(Context context) {
+        mContext = context;
+        mServiceBinder =
+                ServiceBinder.getServiceBinder(
+                        context,
+                        AdServicesCommon.ACTION_MEASUREMENT_SERVICE,
+                        IMeasurementService.Stub::asInterface);
+        mAdIdManager = new AdIdCompatibleManager(context);
+    }
+
+    /**
+     * Retrieves an {@link IMeasurementService} implementation
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    @NonNull
+    public IMeasurementService getService() throws IllegalStateException {
+        IMeasurementService service = mServiceBinder.getService();
+        if (service == null) {
+            throw new IllegalStateException("Unable to find the service");
+        }
+        return service;
+    }
+
+    /** Checks if Ad ID permission is enabled. */
+    private boolean isAdIdPermissionEnabled(AdId adId) {
+        return !AdId.ZERO_OUT.equals(adId.getAdId());
+    }
+
+    /**
+     * Register an attribution source / trigger.
+     *
+     * @hide
+     */
+    private void register(
+            @NonNull RegistrationRequest registrationRequest,
+            @NonNull IMeasurementService service,
+            @Nullable @CallbackExecutor Executor executor,
+            @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
+        Objects.requireNonNull(registrationRequest);
+        requireExecutorForCallback(executor, callback);
+
+        String registrationType = "source";
+        if (registrationRequest.getRegistrationType() == RegistrationRequest.REGISTER_TRIGGER) {
+            registrationType = "trigger";
+        }
+        LogUtil.d("Registering " + registrationType);
+
+        try {
+            service.register(
+                    registrationRequest,
+                    generateCallerMetadataWithCurrentTime(),
+                    new IMeasurementCallback.Stub() {
+                        @Override
+                        public void onResult() {
+                            if (callback != null) {
+                                executor.execute(() -> callback.onResult(new Object()));
+                            }
+                        }
+
+                        @Override
+                        public void onFailure(MeasurementErrorResponse failureParcel) {
+                            if (callback != null) {
+                                executor.execute(
+                                        () ->
+                                                callback.onError(
+                                                        AdServicesStatusUtils.asException(
+                                                                failureParcel)));
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            LogUtil.e(e, "RemoteException");
+            if (callback != null) {
+                executor.execute(() -> callback.onError(new IllegalStateException(e)));
+            }
+        }
+    }
+
+    /**
+     * Register an attribution source (click or view).
+     *
+     * @param attributionSource the platform issues a request to this URI in order to fetch metadata
+     *     associated with the attribution source. The source metadata is stored on device, making
+     *     it eligible to be matched to future triggers.
+     * @param inputEvent either an {@link InputEvent} object (for a click event) or null (for a view
+     *     event).
+     * @param executor used by callback to dispatch results.
+     * @param callback intended to notify asynchronously the API result.
+     */
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void registerSource(
+            @NonNull Uri attributionSource,
+            @Nullable InputEvent inputEvent,
+            @Nullable @CallbackExecutor Executor executor,
+            @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
+        Objects.requireNonNull(attributionSource);
+        requireExecutorForCallback(executor, callback);
+
+        IMeasurementService service = getServiceWrapper(executor, callback);
+
+        if (service == null) {
+            // Error was sent in the callback by getServiceWrapper call
+            LogUtil.d("Measurement service not found");
+            return;
+        }
+
+        final RegistrationRequest.Builder builder =
+                new RegistrationRequest.Builder(
+                                RegistrationRequest.REGISTER_SOURCE,
+                                attributionSource,
+                                getAppPackageName(),
+                                getSdkPackageName())
+                        .setRequestTime(SystemClock.uptimeMillis())
+                        .setInputEvent(inputEvent);
+        // TODO(b/281546062): Can probably remove isAdIdEnabled, since whether adIdValue is null or
+        //  not will determine if adId is enabled.
+        getAdId(
+                (isAdIdEnabled, adIdValue) ->
+                        register(
+                                builder.setAdIdPermissionGranted(isAdIdEnabled)
+                                        .setAdIdValue(adIdValue)
+                                        .build(),
+                                service,
+                                executor,
+                                callback));
+    }
+
+    /**
+     * Register attribution sources(click or view) from an app context. This API will not process
+     * any redirects, all registration URLs should be supplied with the request.
+     *
+     * @param request app source registration request
+     * @param executor used by callback to dispatch results
+     * @param callback intended to notify asynchronously the API result
+     */
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void registerSource(
+            @NonNull SourceRegistrationRequest request,
+            @Nullable @CallbackExecutor Executor executor,
+            @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
+        Objects.requireNonNull(request);
+        requireExecutorForCallback(executor, callback);
+
+        IMeasurementService service = getServiceWrapper(executor, callback);
+        if (service == null) {
+            // Error was sent in the callback by getServiceWrapper call
+            LogUtil.d("Measurement service not found");
+            return;
+        }
+
+        CallerMetadata callerMetadata = generateCallerMetadataWithCurrentTime();
+        IMeasurementCallback measurementCallback =
+                new IMeasurementCallback.Stub() {
+                    @Override
+                    public void onResult() {
+                        if (callback != null) {
+                            executor.execute(() -> callback.onResult(new Object()));
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(MeasurementErrorResponse failureParcel) {
+                        if (callback != null) {
+                            executor.execute(
+                                    () ->
+                                            callback.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    }
+                };
+
+        final SourceRegistrationRequestInternal.Builder builder =
+                new SourceRegistrationRequestInternal.Builder(
+                        request,
+                        getAppPackageName(),
+                        getSdkPackageName(),
+                        SystemClock.uptimeMillis());
+
+        getAdId(
+                (isAdIdEnabled, adIdValue) -> {
+                    try {
+                        LogUtil.d("Registering app sources");
+                        service.registerSource(
+                                builder.setAdIdValue(adIdValue).build(),
+                                callerMetadata,
+                                measurementCallback);
+                    } catch (RemoteException e) {
+                        LogUtil.e(e, "RemoteException");
+                        if (callback != null) {
+                            executor.execute(() -> callback.onError(new IllegalStateException(e)));
+                        }
+                    }
+                });
+    }
+
+    /**
+     * Register an attribution source(click or view) from web context. This API will not process any
+     * redirects, all registration URLs should be supplied with the request. At least one of
+     * appDestination or webDestination parameters are required to be provided. If the registration
+     * is successful, {@code callback}'s {@link AdServicesOutcomeReceiver#onResult} is invoked with
+     * null. In case of failure, a {@link Exception} is sent through {@code callback}'s {@link
+     * AdServicesOutcomeReceiver#onError}. Both success and failure feedback are executed on the
+     * provided {@link Executor}.
+     *
+     * @param request source registration request
+     * @param executor used by callback to dispatch results.
+     * @param callback intended to notify asynchronously the API result.
+     */
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void registerWebSource(
+            @NonNull WebSourceRegistrationRequest request,
+            @Nullable Executor executor,
+            @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
+        Objects.requireNonNull(request);
+        requireExecutorForCallback(executor, callback);
+
+        IMeasurementService service = getServiceWrapper(executor, callback);
+
+        if (service == null) {
+            // Error was sent in the callback by getServiceWrapper call
+            LogUtil.d("Measurement service not found");
+            return;
+        }
+
+        CallerMetadata callerMetadata = generateCallerMetadataWithCurrentTime();
+        IMeasurementCallback measurementCallback =
+                new IMeasurementCallback.Stub() {
+                    @Override
+                    public void onResult() {
+                        if (callback != null) {
+                            executor.execute(() -> callback.onResult(new Object()));
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(MeasurementErrorResponse failureParcel) {
+                        if (callback != null) {
+                            executor.execute(
+                                    () ->
+                                            callback.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    }
+                };
+
+        final WebSourceRegistrationRequestInternal.Builder builder =
+                new WebSourceRegistrationRequestInternal.Builder(
+                        request,
+                        getAppPackageName(),
+                        getSdkPackageName(),
+                        SystemClock.uptimeMillis());
+
+        getAdId(
+                (isAdIdEnabled, adIdValue) ->
+                        registerWebSourceWrapper(
+                                builder.setAdIdPermissionGranted(isAdIdEnabled).build(),
+                                service,
+                                executor,
+                                callerMetadata,
+                                measurementCallback,
+                                callback));
+    }
+
+    /** Wrapper method for registerWebSource. */
+    private void registerWebSourceWrapper(
+            @NonNull WebSourceRegistrationRequestInternal request,
+            @NonNull IMeasurementService service,
+            @Nullable Executor executor,
+            @NonNull CallerMetadata callerMetadata,
+            @NonNull IMeasurementCallback measurementCallback,
+            @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
+        requireExecutorForCallback(executor, callback);
+        try {
+            LogUtil.d("Registering web source");
+            service.registerWebSource(request, callerMetadata, measurementCallback);
+        } catch (RemoteException e) {
+            LogUtil.e(e, "RemoteException");
+            if (callback != null) {
+                executor.execute(() -> callback.onError(new IllegalStateException(e)));
+            }
+        }
+    }
+
+    /**
+     * Register an attribution trigger(click or view) from web context. This API will not process
+     * any redirects, all registration URLs should be supplied with the request. If the registration
+     * is successful, {@code callback}'s {@link AdServicesOutcomeReceiver#onResult} is invoked with
+     * null. In case of failure, a {@link Exception} is sent through {@code callback}'s {@link
+     * AdServicesOutcomeReceiver#onError}. Both success and failure feedback are executed on the
+     * provided {@link Executor}.
+     *
+     * @param request trigger registration request
+     * @param executor used by callback to dispatch results
+     * @param callback intended to notify asynchronously the API result
+     */
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void registerWebTrigger(
+            @NonNull WebTriggerRegistrationRequest request,
+            @Nullable Executor executor,
+            @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
+        Objects.requireNonNull(request);
+        requireExecutorForCallback(executor, callback);
+
+        IMeasurementService service = getServiceWrapper(executor, callback);
+
+        if (service == null) {
+            // Error was sent in the callback by getServiceWrapper call
+            LogUtil.d("Measurement service not found");
+            return;
+        }
+
+        CallerMetadata callerMetadata = generateCallerMetadataWithCurrentTime();
+        IMeasurementCallback measurementCallback =
+                new IMeasurementCallback.Stub() {
+                    @Override
+                    public void onResult() {
+                        if (callback != null) {
+                            executor.execute(() -> callback.onResult(new Object()));
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(MeasurementErrorResponse failureParcel) {
+                        if (callback != null) {
+                            executor.execute(
+                                    () ->
+                                            callback.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    }
+                };
+
+        WebTriggerRegistrationRequestInternal.Builder builder =
+                new WebTriggerRegistrationRequestInternal.Builder(
+                        request, getAppPackageName(), getSdkPackageName());
+
+        getAdId(
+                (isAdIdEnabled, adIdValue) ->
+                        registerWebTriggerWrapper(
+                                builder.setAdIdPermissionGranted(isAdIdEnabled).build(),
+                                service,
+                                executor,
+                                callerMetadata,
+                                measurementCallback,
+                                callback));
+    }
+
+    /** Wrapper method for registerWebTrigger. */
+    private void registerWebTriggerWrapper(
+            @NonNull WebTriggerRegistrationRequestInternal request,
+            @NonNull IMeasurementService service,
+            @Nullable Executor executor,
+            @NonNull CallerMetadata callerMetadata,
+            @NonNull IMeasurementCallback measurementCallback,
+            @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
+        requireExecutorForCallback(executor, callback);
+        try {
+            LogUtil.d("Registering web trigger");
+            service.registerWebTrigger(request, callerMetadata, measurementCallback);
+        } catch (RemoteException e) {
+            LogUtil.e(e, "RemoteException");
+            if (callback != null) {
+                executor.execute(() -> callback.onError(new IllegalStateException(e)));
+            }
+        }
+    }
+
+    /**
+     * Register a trigger (conversion).
+     *
+     * @param trigger the API issues a request to this URI to fetch metadata associated with the
+     *     trigger. The trigger metadata is stored on-device, and is eligible to be matched with
+     *     sources during the attribution process.
+     * @param executor used by callback to dispatch results.
+     * @param callback intended to notify asynchronously the API result.
+     */
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void registerTrigger(
+            @NonNull Uri trigger,
+            @Nullable @CallbackExecutor Executor executor,
+            @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
+        Objects.requireNonNull(trigger);
+        requireExecutorForCallback(executor, callback);
+
+        IMeasurementService service = getServiceWrapper(executor, callback);
+
+        if (service == null) {
+            // Error was sent in the callback by getServiceWrapper call
+            LogUtil.d("Measurement service not found");
+            return;
+        }
+
+        final RegistrationRequest.Builder builder =
+                new RegistrationRequest.Builder(
+                        RegistrationRequest.REGISTER_TRIGGER,
+                        trigger,
+                        getAppPackageName(),
+                        getSdkPackageName());
+        // TODO(b/281546062)
+        getAdId(
+                (isAdIdEnabled, adIdValue) ->
+                        register(
+                                builder.setAdIdPermissionGranted(isAdIdEnabled)
+                                        .setAdIdValue(adIdValue)
+                                        .build(),
+                                service,
+                                executor,
+                                callback));
+    }
+
+    /**
+     * Delete previously registered data.
+     *
+     * @hide
+     */
+    private void deleteRegistrations(
+            @NonNull DeletionParam deletionParam,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AdServicesOutcomeReceiver<Object, Exception> callback) {
+        Objects.requireNonNull(deletionParam);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+        final IMeasurementService service = getServiceWrapper(executor, callback);
+
+        if (service == null) {
+            // Error was sent in the callback by getServiceWrapper call
+            LogUtil.d("Measurement service not found");
+            return;
+        }
+
+        try {
+            service.deleteRegistrations(
+                    deletionParam,
+                    generateCallerMetadataWithCurrentTime(),
+                    new IMeasurementCallback.Stub() {
+                        @Override
+                        public void onResult() {
+                            executor.execute(() -> callback.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(MeasurementErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            callback.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (RemoteException e) {
+            LogUtil.e(e, "RemoteException");
+            executor.execute(() -> callback.onError(new IllegalStateException(e)));
+        }
+    }
+
+    /**
+     * Delete previous registrations. If the deletion is successful, the callback's {@link
+     * AdServicesOutcomeReceiver#onResult} is invoked with null. In case of failure, a {@link
+     * Exception} is sent through the callback's {@link AdServicesOutcomeReceiver#onError}. Both
+     * success and failure feedback are executed on the provided {@link Executor}.
+     *
+     * @param deletionRequest The request for deleting data.
+     * @param executor The executor to run callback.
+     * @param callback intended to notify asynchronously the API result.
+     */
+    public void deleteRegistrations(
+            @NonNull DeletionRequest deletionRequest,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AdServicesOutcomeReceiver<Object, Exception> callback) {
+        deleteRegistrations(
+                new DeletionParam.Builder(
+                                deletionRequest.getOriginUris(),
+                                deletionRequest.getDomainUris(),
+                                deletionRequest.getStart(),
+                                deletionRequest.getEnd(),
+                                getAppPackageName(),
+                                getSdkPackageName())
+                        .setDeletionMode(deletionRequest.getDeletionMode())
+                        .setMatchBehavior(deletionRequest.getMatchBehavior())
+                        .build(),
+                executor,
+                callback);
+    }
+
+    /**
+     * Get Measurement API status.
+     *
+     * <p>The callback's {@code Integer} value is one of {@code MeasurementApiState}.
+     *
+     * @param executor used by callback to dispatch results.
+     * @param callback intended to notify asynchronously the API result.
+     */
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void getMeasurementApiStatus(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AdServicesOutcomeReceiver<Integer, Exception> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        final IMeasurementService service;
+        try {
+            service = getService();
+        } catch (IllegalStateException e) {
+            LogUtil.e(e, "Failed to bind to measurement service");
+            executor.execute(
+                    () -> callback.onResult(MeasurementManager.MEASUREMENT_API_STATE_DISABLED));
+            return;
+        } catch (RuntimeException e) {
+            LogUtil.e(e, "Unknown failure while binding measurement service");
+            executor.execute(() -> callback.onError(e));
+            return;
+        }
+
+        try {
+            service.getMeasurementApiStatus(
+                    new StatusParam.Builder(getAppPackageName(), getSdkPackageName()).build(),
+                    generateCallerMetadataWithCurrentTime(),
+                    new IMeasurementApiStatusCallback.Stub() {
+                        @Override
+                        public void onResult(int result) {
+                            executor.execute(() -> callback.onResult(result));
+                        }
+                    });
+        } catch (RemoteException e) {
+            LogUtil.e(e, "RemoteException");
+            executor.execute(
+                    () -> callback.onResult(MeasurementManager.MEASUREMENT_API_STATE_DISABLED));
+        } catch (RuntimeException e) {
+            LogUtil.e(e, "Unknown failure while getting measurement status");
+            executor.execute(() -> callback.onError(e));
+        }
+    }
+
+    /**
+     * If the service is in an APK (as opposed to the system service), unbind it from the service to
+     * allow the APK process to die.
+     *
+     * @hide Not sure if we'll need this functionality in the final API. For now, we need it for
+     *     performance testing to simulate "cold-start" situations.
+     */
+    @VisibleForTesting
+    public void unbindFromService() {
+        mServiceBinder.unbindFromService();
+    }
+
+    /** Returns the package name of the app from the SDK or app context */
+    private String getAppPackageName() {
+        SandboxedSdkContext sandboxedSdkContext =
+                SandboxedSdkContextUtils.getAsSandboxedSdkContext(mContext);
+        return sandboxedSdkContext == null
+                ? mContext.getPackageName()
+                : sandboxedSdkContext.getClientPackageName();
+    }
+
+    /** Returns the package name of the sdk from the SDK or empty if no SDK found */
+    private String getSdkPackageName() {
+        SandboxedSdkContext sandboxedSdkContext =
+                SandboxedSdkContextUtils.getAsSandboxedSdkContext(mContext);
+        return sandboxedSdkContext == null ? "" : sandboxedSdkContext.getSdkPackageName();
+    }
+
+    private CallerMetadata generateCallerMetadataWithCurrentTime() {
+        return new CallerMetadata.Builder()
+                .setBinderElapsedTimestamp(SystemClock.elapsedRealtime())
+                .build();
+    }
+
+    /** Get Service wrapper, propagates error to the caller */
+    @Nullable
+    private IMeasurementService getServiceWrapper(
+            @Nullable @CallbackExecutor Executor executor,
+            @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
+        requireExecutorForCallback(executor, callback);
+        IMeasurementService service = null;
+        try {
+            service = getService();
+        } catch (RuntimeException e) {
+            LogUtil.e(e, "Failed binding to measurement service");
+            if (callback != null) {
+                executor.execute(() -> callback.onError(e));
+            }
+        }
+        return service;
+    }
+
+    private static void requireExecutorForCallback(
+            Executor executor, AdServicesOutcomeReceiver<Object, Exception> callback) {
+        if (callback != null && executor == null) {
+            throw new IllegalArgumentException(
+                    "Executor should be provided when callback is provided.");
+        }
+    }
+
+    /* Make AdId call with timeout */
+    @SuppressLint("MissingPermission")
+    private void getAdId(MeasurementAdIdCallback measurementAdIdCallback) {
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        AtomicBoolean isAdIdEnabled = new AtomicBoolean();
+        AtomicReference<String> adIdValue = new AtomicReference<>();
+        mAdIdManager.getAdId(
+                mAdIdExecutor,
+                new AdServicesOutcomeReceiver<>() {
+                    @Override
+                    public void onResult(AdId adId) {
+                        isAdIdEnabled.set(isAdIdPermissionEnabled(adId));
+                        adIdValue.set(adId.getAdId().equals(AdId.ZERO_OUT) ? null : adId.getAdId());
+                        LogUtil.d("AdId permission enabled %b", isAdIdEnabled.get());
+                        countDownLatch.countDown();
+                    }
+
+                    @Override
+                    public void onError(Exception error) {
+                        boolean isExpected =
+                                error instanceof IllegalStateException
+                                        || error instanceof SecurityException;
+                        if (isExpected) {
+                            LogUtil.w(DEBUG_API_WARNING_MESSAGE);
+                        } else {
+                            LogUtil.w(error, DEBUG_API_WARNING_MESSAGE);
+                        }
+
+                        countDownLatch.countDown();
+                    }
+                });
+
+        boolean timedOut = false;
+        try {
+            timedOut = !countDownLatch.await(AD_ID_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            LogUtil.w(e, "InterruptedException while waiting for AdId");
+        }
+        if (timedOut) {
+            LogUtil.w("AdId call timed out");
+        }
+        measurementAdIdCallback.onAdIdCallback(isAdIdEnabled.get(), adIdValue.get());
+    }
+}
diff --git a/android-35/android/adservices/measurement/MeasurementErrorResponse.java b/android-35/android/adservices/measurement/MeasurementErrorResponse.java
new file mode 100644
index 0000000..204ed1f
--- /dev/null
+++ b/android-35/android/adservices/measurement/MeasurementErrorResponse.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.measurement;
+
+import static android.adservices.common.AdServicesStatusUtils.STATUS_SUCCESS;
+
+import android.adservices.common.AdServicesResponse;
+import android.adservices.common.AdServicesStatusUtils;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Represents a generic response for Measurement APIs.
+ *
+ * @hide
+ */
+public final class MeasurementErrorResponse extends AdServicesResponse {
+    @NonNull
+    public static final Creator<MeasurementErrorResponse> CREATOR =
+            new Parcelable.Creator<MeasurementErrorResponse>() {
+                @Override
+                public MeasurementErrorResponse createFromParcel(@NonNull Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new MeasurementErrorResponse(in);
+                }
+
+                @Override
+                public MeasurementErrorResponse[] newArray(int size) {
+                    return new MeasurementErrorResponse[size];
+                }
+            };
+
+    protected MeasurementErrorResponse(@NonNull Builder builder) {
+        super(builder.mStatusCode, builder.mErrorMessage);
+    }
+
+    protected MeasurementErrorResponse(@NonNull Parcel in) {
+        super(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        dest.writeInt(mStatusCode);
+        dest.writeString(mErrorMessage);
+    }
+
+    /**
+     * Builder for {@link MeasurementErrorResponse} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        @AdServicesStatusUtils.StatusCode private int mStatusCode = STATUS_SUCCESS;
+        @Nullable private String mErrorMessage;
+
+        public Builder() {}
+
+        /** Set the Status Code. */
+        @NonNull
+        public MeasurementErrorResponse.Builder setStatusCode(
+                @AdServicesStatusUtils.StatusCode int statusCode) {
+            mStatusCode = statusCode;
+            return this;
+        }
+
+        /** Set the Error Message. */
+        @NonNull
+        public MeasurementErrorResponse.Builder setErrorMessage(@Nullable String errorMessage) {
+            mErrorMessage = errorMessage;
+            return this;
+        }
+
+        /** Builds a {@link MeasurementErrorResponse} instance. */
+        @NonNull
+        public MeasurementErrorResponse build() {
+            return new MeasurementErrorResponse(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/measurement/MeasurementManager.java b/android-35/android/adservices/measurement/MeasurementManager.java
new file mode 100644
index 0000000..d403d84
--- /dev/null
+++ b/android-35/android/adservices/measurement/MeasurementManager.java
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.measurement;
+
+import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION;
+
+import android.adservices.common.AdServicesOutcomeReceiver;
+import android.adservices.common.OutcomeReceiverConverter;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.app.sdksandbox.SandboxedSdkContext;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
+import android.os.OutcomeReceiver;
+import android.view.InputEvent;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.adservices.flags.Flags;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/** MeasurementManager provides APIs to manage source and trigger registrations. */
+public class MeasurementManager {
+    /** @hide */
+    public static final String MEASUREMENT_SERVICE = "measurement_service";
+
+    /**
+     * This state indicates that Measurement APIs are unavailable. Invoking them will result in an
+     * {@link UnsupportedOperationException}.
+     */
+    public static final int MEASUREMENT_API_STATE_DISABLED = 0;
+
+    /**
+     * This state indicates that Measurement APIs are enabled.
+     */
+    public static final int MEASUREMENT_API_STATE_ENABLED = 1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            prefix = "MEASUREMENT_API_STATE_",
+            value = {
+                MEASUREMENT_API_STATE_DISABLED,
+                MEASUREMENT_API_STATE_ENABLED,
+            })
+    public @interface MeasurementApiState {}
+
+    private MeasurementCompatibleManager mImpl;
+
+    /**
+     * Factory method for creating an instance of MeasurementManager.
+     *
+     * @param context The {@link Context} to use
+     * @return A {@link MeasurementManager} instance
+     */
+    @NonNull
+    public static MeasurementManager get(@NonNull Context context) {
+        // On T+, context.getSystemService() does more than just call constructor.
+        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+                ? context.getSystemService(MeasurementManager.class)
+                : new MeasurementManager(context);
+    }
+
+    /**
+     * Create MeasurementManager.
+     *
+     * @hide
+     */
+    public MeasurementManager(Context context) {
+        // In case the MeasurementManager is initiated from inside a sdk_sandbox process the
+        // fields will be immediately rewritten by the initialize method below.
+        initialize(context);
+    }
+
+    /**
+     * Create MeasurementManager
+     *
+     * @param compatibleManager the underlying implementation that can be mocked for tests
+     * @hide
+     */
+    @VisibleForTesting
+    public MeasurementManager(@NonNull MeasurementCompatibleManager compatibleManager) {
+        Objects.requireNonNull(compatibleManager);
+        mImpl = compatibleManager;
+    }
+
+    /**
+     * Initializes {@link MeasurementManager} with the given {@code context}.
+     *
+     * <p>This method is called by the {@link SandboxedSdkContext} to propagate the correct context.
+     * For more information check the javadoc on the {@link
+     * android.app.sdksandbox.SdkSandboxSystemServiceRegistry}.
+     *
+     * @hide
+     * @see android.app.sdksandbox.SdkSandboxSystemServiceRegistry
+     */
+    public MeasurementManager initialize(@NonNull Context context) {
+        mImpl = MeasurementCompatibleManager.get(context);
+        return this;
+    }
+
+    /**
+     * Register an attribution source (click or view).
+     *
+     * @param attributionSource the platform issues a request to this URI in order to fetch metadata
+     *     associated with the attribution source. The source metadata is stored on device, making
+     *     it eligible to be matched to future triggers.
+     * @param inputEvent either an {@link InputEvent} object (for a click event) or null (for a view
+     *     event).
+     * @param executor used by callback to dispatch results.
+     * @param callback intended to notify asynchronously the API result.
+     * @throws IllegalArgumentException if the scheme for {@code attributionSource} is not HTTPS
+     */
+    @RequiresApi(Build.VERSION_CODES.S)
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void registerSource(
+            @NonNull Uri attributionSource,
+            @Nullable InputEvent inputEvent,
+            @Nullable @CallbackExecutor Executor executor,
+            @Nullable OutcomeReceiver<Object, Exception> callback) {
+        mImpl.registerSource(
+                attributionSource,
+                inputEvent,
+                executor,
+                OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
+    }
+
+    /**
+     * Register an attribution source (click or view). For use on Android R or lower.
+     *
+     * @param attributionSource the platform issues a request to this URI in order to fetch metadata
+     *     associated with the attribution source. The source metadata is stored on device, making
+     *     it eligible to be matched to future triggers.
+     * @param inputEvent either an {@link InputEvent} object (for a click event) or null (for a view
+     *     event).
+     * @param executor used by callback to dispatch results.
+     * @param callback intended to notify asynchronously the API result.
+     */
+    @FlaggedApi(Flags.FLAG_ADSERVICES_OUTCOMERECEIVER_R_API_ENABLED)
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void registerSource(
+            @NonNull Uri attributionSource,
+            @Nullable InputEvent inputEvent,
+            @Nullable @CallbackExecutor Executor executor,
+            @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
+        mImpl.registerSource(attributionSource, inputEvent, executor, callback);
+    }
+
+    /**
+     * Register attribution sources(click or view) from an app context. This API will not process
+     * any redirects, all registration URLs should be supplied with the request.
+     *
+     * @param request app source registration request
+     * @param executor used by callback to dispatch results
+     * @param callback intended to notify asynchronously the API result
+     */
+    @RequiresApi(Build.VERSION_CODES.S)
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void registerSource(
+            @NonNull SourceRegistrationRequest request,
+            @Nullable @CallbackExecutor Executor executor,
+            @Nullable OutcomeReceiver<Object, Exception> callback) {
+        mImpl.registerSource(
+                request, executor, OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
+    }
+
+    /**
+     * Register attribution sources(click or view) from an app context. This API will not process
+     * any redirects, all registration URLs should be supplied with the request. For use on Android
+     * R or lower.
+     *
+     * @param request app source registration request
+     * @param executor used by callback to dispatch results
+     * @param callback intended to notify asynchronously the API result
+     */
+    @FlaggedApi(Flags.FLAG_ADSERVICES_OUTCOMERECEIVER_R_API_ENABLED)
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void registerSource(
+            @NonNull SourceRegistrationRequest request,
+            @Nullable @CallbackExecutor Executor executor,
+            @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
+        mImpl.registerSource(request, executor, callback);
+    }
+
+    /**
+     * Register an attribution source(click or view) from web context. This API will not process any
+     * redirects, all registration URLs should be supplied with the request. At least one of
+     * appDestination or webDestination parameters are required to be provided. If the registration
+     * is successful, {@code callback}'s {@link OutcomeReceiver#onResult} is invoked with null. In
+     * case of failure, a {@link Exception} is sent through {@code callback}'s {@link
+     * OutcomeReceiver#onError}. Both success and failure feedback are executed on the provided
+     * {@link Executor}.
+     *
+     * @param request source registration request
+     * @param executor used by callback to dispatch results.
+     * @param callback intended to notify asynchronously the API result.
+     */
+    @RequiresApi(Build.VERSION_CODES.S)
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void registerWebSource(
+            @NonNull WebSourceRegistrationRequest request,
+            @Nullable Executor executor,
+            @Nullable OutcomeReceiver<Object, Exception> callback) {
+        mImpl.registerWebSource(
+                request, executor, OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
+    }
+
+    /**
+     * Register an attribution source(click or view) from web context. This API will not process any
+     * redirects, all registration URLs should be supplied with the request. At least one of
+     * appDestination or webDestination parameters are required to be provided. If the registration
+     * is successful, {@code callback}'s {@link OutcomeReceiver#onResult} is invoked with null. In
+     * case of failure, a {@link Exception} is sent through {@code callback}'s {@link
+     * OutcomeReceiver#onError}. Both success and failure feedback are executed on the provided
+     * {@link Executor}.
+     *
+     * <p>For use on Android R or lower.
+     *
+     * @param request source registration request
+     * @param executor used by callback to dispatch results.
+     * @param callback intended to notify asynchronously the API result.
+     */
+    @FlaggedApi(Flags.FLAG_ADSERVICES_OUTCOMERECEIVER_R_API_ENABLED)
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void registerWebSource(
+            @NonNull WebSourceRegistrationRequest request,
+            @Nullable Executor executor,
+            @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
+        mImpl.registerWebSource(request, executor, callback);
+    }
+
+    /**
+     * Register an attribution trigger(click or view) from web context. This API will not process
+     * any redirects, all registration URLs should be supplied with the request. If the registration
+     * is successful, {@code callback}'s {@link OutcomeReceiver#onResult} is invoked with null. In
+     * case of failure, a {@link Exception} is sent through {@code callback}'s {@link
+     * OutcomeReceiver#onError}. Both success and failure feedback are executed on the provided
+     * {@link Executor}.
+     *
+     * @param request trigger registration request
+     * @param executor used by callback to dispatch results
+     * @param callback intended to notify asynchronously the API result
+     */
+    @RequiresApi(Build.VERSION_CODES.S)
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void registerWebTrigger(
+            @NonNull WebTriggerRegistrationRequest request,
+            @Nullable Executor executor,
+            @Nullable OutcomeReceiver<Object, Exception> callback) {
+        mImpl.registerWebTrigger(
+                request, executor, OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
+    }
+
+    /**
+     * Register an attribution trigger(click or view) from web context. This API will not process
+     * any redirects, all registration URLs should be supplied with the request. If the registration
+     * is successful, {@code callback}'s {@link OutcomeReceiver#onResult} is invoked with null. In
+     * case of failure, a {@link Exception} is sent through {@code callback}'s {@link
+     * OutcomeReceiver#onError}. Both success and failure feedback are executed on the provided
+     * {@link Executor}.
+     *
+     * <p>For use on Android R or lower.
+     *
+     * @param request trigger registration request
+     * @param executor used by callback to dispatch results
+     * @param callback intended to notify asynchronously the API result
+     */
+    @FlaggedApi(Flags.FLAG_ADSERVICES_OUTCOMERECEIVER_R_API_ENABLED)
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void registerWebTrigger(
+            @NonNull WebTriggerRegistrationRequest request,
+            @Nullable Executor executor,
+            @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
+        mImpl.registerWebTrigger(request, executor, callback);
+    }
+
+    /**
+     * Register a trigger (conversion).
+     *
+     * @param trigger the API issues a request to this URI to fetch metadata associated with the
+     *     trigger. The trigger metadata is stored on-device, and is eligible to be matched with
+     *     sources during the attribution process.
+     * @param executor used by callback to dispatch results.
+     * @param callback intended to notify asynchronously the API result.
+     * @throws IllegalArgumentException if the scheme for {@code trigger} is not HTTPS
+     */
+    @RequiresApi(Build.VERSION_CODES.S)
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void registerTrigger(
+            @NonNull Uri trigger,
+            @Nullable @CallbackExecutor Executor executor,
+            @Nullable OutcomeReceiver<Object, Exception> callback) {
+        mImpl.registerTrigger(
+                trigger, executor, OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
+    }
+
+    /**
+     * Register a trigger (conversion). For use on Android R or lower.
+     *
+     * @param trigger the API issues a request to this URI to fetch metadata associated with the
+     *     trigger. The trigger metadata is stored on-device, and is eligible to be matched with
+     *     sources during the attribution process.
+     * @param executor used by callback to dispatch results.
+     * @param callback intended to notify asynchronously the API result.
+     */
+    @FlaggedApi(Flags.FLAG_ADSERVICES_OUTCOMERECEIVER_R_API_ENABLED)
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void registerTrigger(
+            @NonNull Uri trigger,
+            @Nullable @CallbackExecutor Executor executor,
+            @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
+        mImpl.registerTrigger(trigger, executor, callback);
+    }
+
+    /**
+     * Delete previous registrations. If the deletion is successful, the callback's {@link
+     * OutcomeReceiver#onResult} is invoked with null. In case of failure, a {@link Exception} is
+     * sent through the callback's {@link OutcomeReceiver#onError}. Both success and failure
+     * feedback are executed on the provided {@link Executor}.
+     *
+     * @param deletionRequest The request for deleting data.
+     * @param executor The executor to run callback.
+     * @param callback intended to notify asynchronously the API result.
+     */
+    @RequiresApi(Build.VERSION_CODES.S)
+    public void deleteRegistrations(
+            @NonNull DeletionRequest deletionRequest,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> callback) {
+        mImpl.deleteRegistrations(
+                deletionRequest,
+                executor,
+                OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
+    }
+
+    /**
+     * Delete previous registrations. If the deletion is successful, the callback's {@link
+     * OutcomeReceiver#onResult} is invoked with null. In case of failure, a {@link Exception} is
+     * sent through the callback's {@link OutcomeReceiver#onError}. Both success and failure
+     * feedback are executed on the provided {@link Executor}.
+     *
+     * <p>For use on Android R or lower.
+     *
+     * @param deletionRequest The request for deleting data.
+     * @param executor The executor to run callback.
+     * @param callback intended to notify asynchronously the API result.
+     */
+    @FlaggedApi(Flags.FLAG_ADSERVICES_OUTCOMERECEIVER_R_API_ENABLED)
+    public void deleteRegistrations(
+            @NonNull DeletionRequest deletionRequest,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AdServicesOutcomeReceiver<Object, Exception> callback) {
+        mImpl.deleteRegistrations(deletionRequest, executor, callback);
+    }
+
+    /**
+     * Get Measurement API status.
+     *
+     * <p>The callback's {@code Integer} value is one of {@code MeasurementApiState}.
+     *
+     * @param executor used by callback to dispatch results.
+     * @param callback intended to notify asynchronously the API result.
+     */
+    @RequiresApi(Build.VERSION_CODES.S)
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void getMeasurementApiStatus(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Integer, Exception> callback) {
+        mImpl.getMeasurementApiStatus(
+                executor, OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
+    }
+
+    /**
+     * Get Measurement API status.
+     *
+     * <p>The callback's {@code Integer} value is one of {@code MeasurementApiState}.
+     *
+     * <p>For use on Android R or lower.
+     *
+     * @param executor used by callback to dispatch results.
+     * @param callback intended to notify asynchronously the API result.
+     */
+    @FlaggedApi(Flags.FLAG_ADSERVICES_OUTCOMERECEIVER_R_API_ENABLED)
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    public void getMeasurementApiStatus(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AdServicesOutcomeReceiver<Integer, Exception> callback) {
+        mImpl.getMeasurementApiStatus(executor, callback);
+    }
+
+    /**
+     * If the service is in an APK (as opposed to the system service), unbind it from the service to
+     * allow the APK process to die.
+     *
+     * @hide Not sure if we'll need this functionality in the final API. For now, we need it for
+     *     performance testing to simulate "cold-start" situations.
+     */
+    @VisibleForTesting
+    public void unbindFromService() {
+        mImpl.unbindFromService();
+    }
+}
diff --git a/android-35/android/adservices/measurement/RegistrationRequest.java b/android-35/android/adservices/measurement/RegistrationRequest.java
new file mode 100644
index 0000000..30ba5b6
--- /dev/null
+++ b/android-35/android/adservices/measurement/RegistrationRequest.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.measurement;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.InputEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+
+/**
+ * Class to hold input to measurement registration calls.
+ * @hide
+ */
+public final class RegistrationRequest implements Parcelable {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+        INVALID,
+        REGISTER_SOURCE,
+        REGISTER_TRIGGER,
+    })
+    public @interface RegistrationType {}
+    /** Invalid registration type used as a default. */
+    public static final int INVALID = 0;
+    /**
+     * A request to register an Attribution Source event (NOTE: AdServices type not
+     * android.context.AttributionSource).
+     */
+    public static final int REGISTER_SOURCE = 1;
+    /** A request to register a trigger event. */
+    public static final int REGISTER_TRIGGER = 2;
+
+    @RegistrationType private final int mRegistrationType;
+    private final Uri mRegistrationUri;
+    private final InputEvent mInputEvent;
+    private final String mAppPackageName;
+    private final String mSdkPackageName;
+    private final long mRequestTime;
+    private final boolean mIsAdIdPermissionGranted;
+    private final String mAdIdValue;
+
+    private RegistrationRequest(@NonNull Builder builder) {
+        mRegistrationType = builder.mRegistrationType;
+        mRegistrationUri = builder.mRegistrationUri;
+        mInputEvent = builder.mInputEvent;
+        mAppPackageName = builder.mAppPackageName;
+        mSdkPackageName = builder.mSdkPackageName;
+        mRequestTime = builder.mRequestTime;
+        mIsAdIdPermissionGranted = builder.mIsAdIdPermissionGranted;
+        mAdIdValue = builder.mAdIdValue;
+    }
+
+    /**
+     * Unpack an RegistrationRequest from a Parcel.
+     */
+    private RegistrationRequest(Parcel in) {
+        mRegistrationType = in.readInt();
+        mRegistrationUri = Uri.CREATOR.createFromParcel(in);
+        mAppPackageName = in.readString();
+        mSdkPackageName = in.readString();
+        boolean hasInputEvent = in.readBoolean();
+        if (hasInputEvent) {
+            mInputEvent = InputEvent.CREATOR.createFromParcel(in);
+        } else {
+            mInputEvent = null;
+        }
+        mRequestTime = in.readLong();
+        mIsAdIdPermissionGranted = in.readBoolean();
+        boolean hasAdIdValue = in.readBoolean();
+        if (hasAdIdValue) {
+            mAdIdValue = in.readString();
+        } else {
+            mAdIdValue = null;
+        }
+    }
+
+    /** Creator for Parcelable (via reflection). */
+    @NonNull
+    public static final Parcelable.Creator<RegistrationRequest> CREATOR =
+            new Parcelable.Creator<RegistrationRequest>() {
+                @Override
+                public RegistrationRequest createFromParcel(Parcel in) {
+                    return new RegistrationRequest(in);
+                }
+
+                @Override
+                public RegistrationRequest[] newArray(int size) {
+                    return new RegistrationRequest[size];
+                }
+            };
+
+    /**
+     * For Parcelable, no special marshalled objects.
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * For Parcelable, write out to a Parcel in particular order.
+     */
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        Objects.requireNonNull(out);
+        out.writeInt(mRegistrationType);
+        mRegistrationUri.writeToParcel(out, flags);
+        out.writeString(mAppPackageName);
+        out.writeString(mSdkPackageName);
+        if (mInputEvent != null) {
+            out.writeBoolean(true);
+            mInputEvent.writeToParcel(out, flags);
+        } else {
+            out.writeBoolean(false);
+        }
+        out.writeLong(mRequestTime);
+        out.writeBoolean(mIsAdIdPermissionGranted);
+        if (mAdIdValue != null) {
+            out.writeBoolean(true);
+            out.writeString(mAdIdValue);
+        } else {
+            out.writeBoolean(false);
+        }
+    }
+
+    /** Type of the registration. */
+    @RegistrationType
+    public int getRegistrationType() {
+        return mRegistrationType;
+    }
+
+    /** Source URI of the App / Publisher. */
+    @NonNull
+    public Uri getRegistrationUri() {
+        return mRegistrationUri;
+    }
+
+    /** InputEvent related to an ad event. */
+    @Nullable
+    public InputEvent getInputEvent() {
+        return mInputEvent;
+    }
+
+    /** Package name of the app used for the registration. */
+    @NonNull
+    public String getAppPackageName() {
+        return mAppPackageName;
+    }
+
+    /** Package name of the sdk used for the registration. */
+    @NonNull
+    public String getSdkPackageName() {
+        return mSdkPackageName;
+    }
+
+    /** Time the request was created, as millis since boot excluding time in deep sleep. */
+    @NonNull
+    public long getRequestTime() {
+        return mRequestTime;
+    }
+
+    /** Ad ID Permission */
+    @NonNull
+    public boolean isAdIdPermissionGranted() {
+        return mIsAdIdPermissionGranted;
+    }
+
+    /** Ad ID Value */
+    @Nullable
+    public String getAdIdValue() {
+        return mAdIdValue;
+    }
+
+    /**
+     * A builder for {@link RegistrationRequest}.
+     */
+    public static final class Builder {
+        @RegistrationType private final int mRegistrationType;
+        private final Uri mRegistrationUri;
+        private final String mAppPackageName;
+        private final String mSdkPackageName;
+        private InputEvent mInputEvent;
+        private long mRequestTime;
+        private boolean mIsAdIdPermissionGranted;
+        private String mAdIdValue;
+
+        /**
+         * Builder constructor for {@link RegistrationRequest}.
+         *
+         * @param type registration type, either source or trigger
+         * @param registrationUri registration uri endpoint for registering a source/trigger
+         * @param appPackageName app package name that is calling PP API
+         * @param sdkPackageName sdk package name that is calling PP API
+         * @throws IllegalArgumentException if the scheme for {@code registrationUri} is not HTTPS
+         * or if {@code type} is not one of {@code REGISTER_SOURCE} or {@code REGISTER_TRIGGER}
+         */
+        public Builder(
+                @RegistrationType int type,
+                @NonNull Uri registrationUri,
+                @NonNull String appPackageName,
+                @NonNull String sdkPackageName) {
+            if (type != REGISTER_SOURCE && type != REGISTER_TRIGGER) {
+                throw new IllegalArgumentException("Invalid registrationType");
+            }
+
+            Objects.requireNonNull(registrationUri);
+            if (registrationUri.getScheme() == null
+                    || !registrationUri.getScheme().equalsIgnoreCase("https")) {
+                throw new IllegalArgumentException("registrationUri must have an HTTPS scheme");
+            }
+
+            Objects.requireNonNull(appPackageName);
+            Objects.requireNonNull(sdkPackageName);
+            mRegistrationType = type;
+            mRegistrationUri = registrationUri;
+            mAppPackageName = appPackageName;
+            mSdkPackageName = sdkPackageName;
+        }
+
+        /** See {@link RegistrationRequest#getInputEvent}. */
+        @NonNull
+        public Builder setInputEvent(@Nullable InputEvent event) {
+            mInputEvent = event;
+            return this;
+        }
+
+        /** See {@link RegistrationRequest#getRequestTime}. */
+        @NonNull
+        public Builder setRequestTime(long requestTime) {
+            mRequestTime = requestTime;
+            return this;
+        }
+
+        /** See {@link RegistrationRequest#isAdIdPermissionGranted()}. */
+        @NonNull
+        public Builder setAdIdPermissionGranted(boolean adIdPermissionGranted) {
+            mIsAdIdPermissionGranted = adIdPermissionGranted;
+            return this;
+        }
+
+        /** See {@link RegistrationRequest#getAdIdValue()}. */
+        @NonNull
+        public Builder setAdIdValue(@Nullable String adIdValue) {
+            mAdIdValue = adIdValue;
+            return this;
+        }
+
+        /** Build the RegistrationRequest. */
+        @NonNull
+        public RegistrationRequest build() {
+            // Ensure registrationType has been set,
+            // throws IllegalArgumentException if mRegistrationType
+            // isn't a valid choice.
+            if (mRegistrationType != REGISTER_SOURCE && mRegistrationType != REGISTER_TRIGGER) {
+                throw new IllegalArgumentException("Invalid registrationType");
+            }
+
+            return new RegistrationRequest(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/measurement/SourceRegistrationRequest.java b/android-35/android/adservices/measurement/SourceRegistrationRequest.java
new file mode 100644
index 0000000..57b2e7b
--- /dev/null
+++ b/android-35/android/adservices/measurement/SourceRegistrationRequest.java
@@ -0,0 +1,182 @@
+/*
+ * 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 android.adservices.measurement;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.InputEvent;
+
+import com.android.adservices.AdServicesParcelableUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Class to hold input to measurement source registration calls.
+ */
+public final class SourceRegistrationRequest implements Parcelable {
+    private static final int REGISTRATION_URIS_MAX_COUNT = 20;
+    /** Registration URIs to fetch sources. */
+    @NonNull private final List<Uri> mRegistrationUris;
+
+    /**
+     * User Interaction {@link InputEvent} used by the AttributionReporting API to distinguish
+     * clicks from views. It will be an {@link InputEvent} object (for a click event) or null (for a
+     * view event).
+     */
+    @Nullable private final InputEvent mInputEvent;
+
+    private SourceRegistrationRequest(@NonNull Builder builder) {
+        mRegistrationUris = builder.mRegistrationUris;
+        mInputEvent = builder.mInputEvent;
+    }
+
+    private SourceRegistrationRequest(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+        List<Uri> registrationsUris = new ArrayList<>();
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+            in.readList(registrationsUris, Uri.class.getClassLoader());
+        } else {
+            in.readList(registrationsUris, Uri.class.getClassLoader(), Uri.class);
+        }
+        mRegistrationUris = registrationsUris;
+        mInputEvent =
+                AdServicesParcelableUtil.readNullableFromParcel(
+                        in, InputEvent.CREATOR::createFromParcel);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SourceRegistrationRequest)) return false;
+        SourceRegistrationRequest that = (SourceRegistrationRequest) o;
+        return Objects.equals(mRegistrationUris, that.mRegistrationUris)
+                && Objects.equals(mInputEvent, that.mInputEvent);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRegistrationUris, mInputEvent);
+    }
+
+    /** Registration URIs to fetch sources. */
+    @NonNull
+    public List<Uri> getRegistrationUris() {
+        return mRegistrationUris;
+    }
+
+    /**
+     * User Interaction {@link InputEvent} used by the AttributionReporting API to distinguish
+     * clicks from views. It will be an {@link InputEvent} object (for a click event) or null (for a
+     * view event)
+     */
+    @Nullable
+    public InputEvent getInputEvent() {
+        return mInputEvent;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        Objects.requireNonNull(out);
+        out.writeList(mRegistrationUris);
+        AdServicesParcelableUtil.writeNullableToParcel(
+                out, mInputEvent, (target, event) -> event.writeToParcel(target, flags));
+    }
+
+    /** Builder for {@link SourceRegistrationRequest}. */
+    public static final class Builder {
+        /** Registration {@link Uri}s to fetch sources. */
+        @NonNull private final List<Uri> mRegistrationUris;
+        /**
+         * User Interaction InputEvent used by the attribution reporting API to distinguish clicks
+         * from views.
+         */
+        @Nullable private InputEvent mInputEvent;
+
+        /**
+         * Builder constructor for {@link SourceRegistrationRequest}.
+         *
+         * @param registrationUris source registration {@link Uri}s
+         * @throws IllegalArgumentException if the scheme for one or more of the
+         * {@code registrationUris} is not HTTPS
+         */
+        public Builder(@NonNull List<Uri> registrationUris) {
+            Objects.requireNonNull(registrationUris);
+            if (registrationUris.isEmpty()
+                    || registrationUris.size() > REGISTRATION_URIS_MAX_COUNT) {
+                throw new IllegalArgumentException(
+                        String.format(
+                                "Requests should have at least 1 and at most %d URIs."
+                                        + " Request has %d URIs.",
+                                REGISTRATION_URIS_MAX_COUNT, registrationUris.size()));
+            }
+            for (Uri registrationUri : registrationUris) {
+                if (registrationUri.getScheme() == null
+                        || !registrationUri.getScheme().equalsIgnoreCase("https")) {
+                    throw new IllegalArgumentException(
+                            "registrationUri must have an HTTPS scheme");
+                }
+            }
+            mRegistrationUris = registrationUris;
+        }
+
+        /**
+         * Setter corresponding to {@link #getInputEvent()}.
+         *
+         * @param inputEvent User Interaction {@link InputEvent} used by the AttributionReporting
+         *     API to distinguish clicks from views. It will be an {@link InputEvent} object (for a
+         *     click event) or null (for a view event)
+         * @return builder
+         */
+        @NonNull
+        public Builder setInputEvent(@Nullable InputEvent inputEvent) {
+            mInputEvent = inputEvent;
+            return this;
+        }
+
+        /** Pre-validates parameters and builds {@link SourceRegistrationRequest}. */
+        @NonNull
+        public SourceRegistrationRequest build() {
+            return new SourceRegistrationRequest(this);
+        }
+    }
+
+    /** Creator for Paracelable (via reflection). */
+    @NonNull
+    public static final Parcelable.Creator<SourceRegistrationRequest> CREATOR =
+            new Parcelable.Creator<>() {
+                @Override
+                public SourceRegistrationRequest createFromParcel(Parcel in) {
+                    return new SourceRegistrationRequest(in);
+                }
+
+                @Override
+                public SourceRegistrationRequest[] newArray(int size) {
+                    return new SourceRegistrationRequest[size];
+                }
+            };
+}
diff --git a/android-35/android/adservices/measurement/SourceRegistrationRequestInternal.java b/android-35/android/adservices/measurement/SourceRegistrationRequestInternal.java
new file mode 100644
index 0000000..25e2ee5
--- /dev/null
+++ b/android-35/android/adservices/measurement/SourceRegistrationRequestInternal.java
@@ -0,0 +1,189 @@
+/*
+ * 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 android.adservices.measurement;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.adservices.AdServicesParcelableUtil;
+
+import java.util.Objects;
+
+/**
+ * Internal source registration request object to communicate from {@link MeasurementManager} to
+ * {@link IMeasurementService}.
+ *
+ * @hide
+ */
+public class SourceRegistrationRequestInternal implements Parcelable {
+    /** Holds input to measurement source registration calls. */
+    @NonNull private final SourceRegistrationRequest mSourceRegistrationRequest;
+    /** Caller app package name. */
+    @NonNull private final String mAppPackageName;
+    /** Calling SDK package name. */
+    @NonNull private final String mSdkPackageName;
+    /** Time the request was created, in millis since boot excluding time in deep sleep. */
+    private final long mBootRelativeRequestTime;
+    /** Ad ID value if the permission is granted, null otherwise. */
+    @Nullable private final String mAdIdValue;
+
+    private SourceRegistrationRequestInternal(@NonNull Builder builder) {
+        mSourceRegistrationRequest = builder.mRegistrationRequest;
+        mAppPackageName = builder.mAppPackageName;
+        mSdkPackageName = builder.mSdkPackageName;
+        mBootRelativeRequestTime = builder.mBootRelativeRequestTime;
+        mAdIdValue = builder.mAdIdValue;
+    }
+
+    private SourceRegistrationRequestInternal(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+        mSourceRegistrationRequest = SourceRegistrationRequest.CREATOR.createFromParcel(in);
+        mAppPackageName = in.readString();
+        mSdkPackageName = in.readString();
+        mBootRelativeRequestTime = in.readLong();
+        mAdIdValue = AdServicesParcelableUtil.readNullableFromParcel(in, Parcel::readString);
+    }
+
+    /** Holds input to measurement source registration calls from app context. */
+    @NonNull
+    public SourceRegistrationRequest getSourceRegistrationRequest() {
+        return mSourceRegistrationRequest;
+    }
+
+    /** Caller app package name. */
+    @NonNull
+    public String getAppPackageName() {
+        return mAppPackageName;
+    }
+
+    /** Calling SDK package name. */
+    @NonNull
+    public String getSdkPackageName() {
+        return mSdkPackageName;
+    }
+
+    /** Time the request was created, in millis since boot excluding time in deep sleep. */
+    @NonNull
+    public long getBootRelativeRequestTime() {
+        return mBootRelativeRequestTime;
+    }
+
+    /** Ad ID value if the permission is granted, null otherwise. */
+    @Nullable
+    public String getAdIdValue() {
+        return mAdIdValue;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SourceRegistrationRequestInternal)) return false;
+        SourceRegistrationRequestInternal that = (SourceRegistrationRequestInternal) o;
+        return Objects.equals(mSourceRegistrationRequest, that.mSourceRegistrationRequest)
+                && Objects.equals(mAppPackageName, that.mAppPackageName)
+                && Objects.equals(mSdkPackageName, that.mSdkPackageName)
+                && mBootRelativeRequestTime == that.mBootRelativeRequestTime
+                && Objects.equals(mAdIdValue, that.mAdIdValue);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mSourceRegistrationRequest,
+                mAppPackageName,
+                mSdkPackageName,
+                mBootRelativeRequestTime,
+                mAdIdValue);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        Objects.requireNonNull(out);
+        mSourceRegistrationRequest.writeToParcel(out, flags);
+        out.writeString(mAppPackageName);
+        out.writeString(mSdkPackageName);
+        out.writeLong(mBootRelativeRequestTime);
+        AdServicesParcelableUtil.writeNullableToParcel(out, mAdIdValue, Parcel::writeString);
+    }
+
+    /** Builder for {@link SourceRegistrationRequestInternal}. */
+    public static final class Builder {
+        /** External source registration request from client app SDK. */
+        @NonNull private final SourceRegistrationRequest mRegistrationRequest;
+        /** Package name of the app used for the registration. Used to determine the registrant. */
+        @NonNull private final String mAppPackageName;
+        /** Package name of the sdk used for the registration. */
+        @NonNull private final String mSdkPackageName;
+        /** Time the request was created, in millis since boot excluding time in deep sleep. */
+        private final long mBootRelativeRequestTime;
+        /** AD ID value if the permission was granted. */
+        @Nullable private String mAdIdValue;
+        /**
+         * Builder constructor for {@link SourceRegistrationRequestInternal}.
+         *
+         * @param registrationRequest external source registration request
+         * @param appPackageName app package name that is calling PP API
+         * @param sdkPackageName sdk package name that is calling PP API
+         */
+        public Builder(
+                @NonNull SourceRegistrationRequest registrationRequest,
+                @NonNull String appPackageName,
+                @NonNull String sdkPackageName,
+                long bootRelativeRequestTime) {
+            Objects.requireNonNull(registrationRequest);
+            Objects.requireNonNull(appPackageName);
+            Objects.requireNonNull(sdkPackageName);
+            mRegistrationRequest = registrationRequest;
+            mAppPackageName = appPackageName;
+            mSdkPackageName = sdkPackageName;
+            mBootRelativeRequestTime = bootRelativeRequestTime;
+        }
+
+        /** Pre-validates parameters and builds {@link SourceRegistrationRequestInternal}. */
+        @NonNull
+        public SourceRegistrationRequestInternal build() {
+            return new SourceRegistrationRequestInternal(this);
+        }
+
+        /** See {@link SourceRegistrationRequestInternal#getAdIdValue()}. */
+        public SourceRegistrationRequestInternal.Builder setAdIdValue(@Nullable String adIdValue) {
+            mAdIdValue = adIdValue;
+            return this;
+        }
+    }
+
+    /** Creator for Parcelable (via reflection). */
+    public static final Creator<SourceRegistrationRequestInternal> CREATOR =
+            new Creator<>() {
+                @Override
+                public SourceRegistrationRequestInternal createFromParcel(Parcel in) {
+                    return new SourceRegistrationRequestInternal(in);
+                }
+
+                @Override
+                public SourceRegistrationRequestInternal[] newArray(int size) {
+                    return new SourceRegistrationRequestInternal[size];
+                }
+            };
+}
diff --git a/android-35/android/adservices/measurement/StatusParam.java b/android-35/android/adservices/measurement/StatusParam.java
new file mode 100644
index 0000000..a7b3e94
--- /dev/null
+++ b/android-35/android/adservices/measurement/StatusParam.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.measurement;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Class to hold parameters needed for getting the Measurement API status. This is an internal class
+ * for communication between the {@link MeasurementManager} and {@link IMeasurementService} impl.
+ *
+ * @hide
+ */
+public final class StatusParam implements Parcelable {
+    private final String mAppPackageName;
+    private final String mSdkPackageName;
+
+    private StatusParam(@NonNull Builder builder) {
+        mAppPackageName = builder.mAppPackageName;
+        mSdkPackageName = builder.mSdkPackageName;
+    }
+
+    /** Unpack an StatusParam from a Parcel. */
+    private StatusParam(Parcel in) {
+        mAppPackageName = in.readString();
+        mSdkPackageName = in.readString();
+    }
+
+    /** Creator for Parcelable (via reflection). */
+    @NonNull
+    public static final Creator<StatusParam> CREATOR =
+            new Creator<StatusParam>() {
+                @Override
+                public StatusParam createFromParcel(Parcel in) {
+                    return new StatusParam(in);
+                }
+
+                @Override
+                public StatusParam[] newArray(int size) {
+                    return new StatusParam[size];
+                }
+            };
+
+    /** For Parcelable, no special marshalled objects. */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** For Parcelable, write out to a Parcel in particular order. */
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        Objects.requireNonNull(out);
+        out.writeString(mAppPackageName);
+        out.writeString(mSdkPackageName);
+    }
+
+    /** Package name of the app used for getting the status. */
+    @NonNull
+    public String getAppPackageName() {
+        return mAppPackageName;
+    }
+
+    /** Package name of the sdk used for getting the status. */
+    @NonNull
+    public String getSdkPackageName() {
+        return mSdkPackageName;
+    }
+
+    /** A builder for {@link StatusParam}. */
+    public static final class Builder {
+        private final String mAppPackageName;
+        private final String mSdkPackageName;
+
+        /**
+         * Builder constructor for {@link StatusParam}.
+         *
+         * @param appPackageName see {@link StatusParam#getAppPackageName()}
+         * @param sdkPackageName see {@link StatusParam#getSdkPackageName()}
+         */
+        public Builder(@NonNull String appPackageName, @NonNull String sdkPackageName) {
+            Objects.requireNonNull(appPackageName);
+            Objects.requireNonNull(sdkPackageName);
+            mAppPackageName = appPackageName;
+            mSdkPackageName = sdkPackageName;
+        }
+
+        /** Build the StatusParam. */
+        @NonNull
+        public StatusParam build() {
+            return new StatusParam(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/measurement/WebSourceParams.java b/android-35/android/adservices/measurement/WebSourceParams.java
new file mode 100644
index 0000000..546220e
--- /dev/null
+++ b/android-35/android/adservices/measurement/WebSourceParams.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.measurement;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/** Class holding source registration parameters. */
+public final class WebSourceParams implements Parcelable {
+    /** Creator for Paracelable (via reflection). */
+    @NonNull
+    public static final Parcelable.Creator<WebSourceParams> CREATOR =
+            new Parcelable.Creator<WebSourceParams>() {
+                @Override
+                public WebSourceParams createFromParcel(Parcel in) {
+                    return new WebSourceParams(in);
+                }
+
+                @Override
+                public WebSourceParams[] newArray(int size) {
+                    return new WebSourceParams[size];
+                }
+            };
+    /**
+     * URI that the Attribution Reporting API sends a request to in order to obtain source
+     * registration parameters.
+     */
+    @NonNull private final Uri mRegistrationUri;
+    /**
+     * Used by the browser to indicate whether the debug key obtained from the registration URI is
+     * allowed to be used
+     */
+    private final boolean mDebugKeyAllowed;
+
+    private WebSourceParams(@NonNull Builder builder) {
+        mRegistrationUri = builder.mRegistrationUri;
+        mDebugKeyAllowed = builder.mDebugKeyAllowed;
+    }
+
+    /** Unpack a SourceRegistration from a Parcel. */
+    private WebSourceParams(@NonNull Parcel in) {
+        mRegistrationUri = Uri.CREATOR.createFromParcel(in);
+        mDebugKeyAllowed = in.readBoolean();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof WebSourceParams)) return false;
+        WebSourceParams that = (WebSourceParams) o;
+        return mDebugKeyAllowed == that.mDebugKeyAllowed
+                && Objects.equals(mRegistrationUri, that.mRegistrationUri);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRegistrationUri, mDebugKeyAllowed);
+    }
+
+    /** Getter for registration Uri. */
+    @NonNull
+    public Uri getRegistrationUri() {
+        return mRegistrationUri;
+    }
+
+    /**
+     * Getter for debug allowed/disallowed flag. Its value as {@code true} means to allow parsing
+     * debug keys from registration responses and their addition in the generated reports.
+     */
+    public boolean isDebugKeyAllowed() {
+        return mDebugKeyAllowed;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        Objects.requireNonNull(out);
+        mRegistrationUri.writeToParcel(out, flags);
+        out.writeBoolean(mDebugKeyAllowed);
+    }
+
+    /** A builder for {@link WebSourceParams}. */
+    public static final class Builder {
+        /**
+         * URI that the Attribution Reporting API sends a request to in order to obtain source
+         * registration parameters.
+         */
+        @NonNull private final Uri mRegistrationUri;
+        /**
+         * Used by the browser to indicate whether the debug key obtained from the registration URI
+         * is allowed to be used
+         */
+        private boolean mDebugKeyAllowed;
+
+        /**
+         * Builder constructor for {@link WebSourceParams}. {@code mIsDebugKeyAllowed} is assigned
+         * false by default.
+         *
+         * @param registrationUri URI that the Attribution Reporting API sends a request to in order
+         *     to obtain source registration parameters.
+         * @throws IllegalArgumentException if the scheme for {@code registrationUri} is not HTTPS
+         */
+        public Builder(@NonNull Uri registrationUri) {
+            Objects.requireNonNull(registrationUri);
+            if (registrationUri.getScheme() == null
+                    || !registrationUri.getScheme().equalsIgnoreCase("https")) {
+                throw new IllegalArgumentException("registrationUri must have an HTTPS scheme");
+            }
+            mRegistrationUri = registrationUri;
+            mDebugKeyAllowed = false;
+        }
+
+        /**
+         * Setter for debug allow/disallow flag. Setting it to true will allow parsing debug keys
+         * from registration responses and their addition in the generated reports.
+         *
+         * @param debugKeyAllowed used by the browser to indicate whether the debug key obtained
+         *     from the registration URI is allowed to be used
+         * @return builder
+         */
+        @NonNull
+        public Builder setDebugKeyAllowed(boolean debugKeyAllowed) {
+            this.mDebugKeyAllowed = debugKeyAllowed;
+            return this;
+        }
+
+        /**
+         * Built immutable {@link WebSourceParams}.
+         *
+         * @return immutable {@link WebSourceParams}
+         */
+        @NonNull
+        public WebSourceParams build() {
+            return new WebSourceParams(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/measurement/WebSourceRegistrationRequest.java b/android-35/android/adservices/measurement/WebSourceRegistrationRequest.java
new file mode 100644
index 0000000..946395a
--- /dev/null
+++ b/android-35/android/adservices/measurement/WebSourceRegistrationRequest.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.measurement;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.InputEvent;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/** Class to hold input to measurement source registration calls from web context. */
+public final class WebSourceRegistrationRequest implements Parcelable {
+    private static final String ANDROID_APP_SCHEME = "android-app";
+    private static final int WEB_SOURCE_PARAMS_MAX_COUNT = 80;
+
+    /** Creator for Paracelable (via reflection). */
+    @NonNull
+    public static final Parcelable.Creator<WebSourceRegistrationRequest> CREATOR =
+            new Parcelable.Creator<WebSourceRegistrationRequest>() {
+                @Override
+                public WebSourceRegistrationRequest createFromParcel(Parcel in) {
+                    return new WebSourceRegistrationRequest(in);
+                }
+
+                @Override
+                public WebSourceRegistrationRequest[] newArray(int size) {
+                    return new WebSourceRegistrationRequest[size];
+                }
+            };
+    /** Registration info to fetch sources. */
+    @NonNull private final List<WebSourceParams> mWebSourceParams;
+
+    /** Top level origin of publisher. */
+    @NonNull private final Uri mTopOriginUri;
+
+    /**
+     * User Interaction {@link InputEvent} used by the AttributionReporting API to distinguish
+     * clicks from views.
+     */
+    @Nullable private final InputEvent mInputEvent;
+
+    /**
+     * App destination of the source. It is the android app {@link Uri} where corresponding
+     * conversion is expected. This field is compared with the corresponding field in Source
+     * Registration Response, if matching fails the registration is rejected. If null is provided,
+     * no destination matching will be performed.
+     */
+    @Nullable private final Uri mAppDestination;
+
+    /**
+     * Web destination of the source. It is the website {@link Uri} where corresponding conversion
+     * is expected. This field is compared with the corresponding field in Source Registration
+     * Response, if matching fails the registration is rejected. If null is provided, no destination
+     * matching will be performed.
+     */
+    @Nullable private final Uri mWebDestination;
+
+    /** Verified destination by the caller. This is where the user actually landed. */
+    @Nullable private final Uri mVerifiedDestination;
+
+    private WebSourceRegistrationRequest(@NonNull Builder builder) {
+        mWebSourceParams = builder.mWebSourceParams;
+        mInputEvent = builder.mInputEvent;
+        mTopOriginUri = builder.mTopOriginUri;
+        mAppDestination = builder.mAppDestination;
+        mWebDestination = builder.mWebDestination;
+        mVerifiedDestination = builder.mVerifiedDestination;
+    }
+
+    private WebSourceRegistrationRequest(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+        ArrayList<WebSourceParams> sourceRegistrations = new ArrayList<>();
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+            in.readList(sourceRegistrations, WebSourceParams.class.getClassLoader());
+        } else {
+            in.readList(
+                    sourceRegistrations,
+                    WebSourceParams.class.getClassLoader(),
+                    WebSourceParams.class);
+        }
+        mWebSourceParams = sourceRegistrations;
+        mTopOriginUri = Uri.CREATOR.createFromParcel(in);
+        if (in.readBoolean()) {
+            mInputEvent = InputEvent.CREATOR.createFromParcel(in);
+        } else {
+            mInputEvent = null;
+        }
+        if (in.readBoolean()) {
+            mAppDestination = Uri.CREATOR.createFromParcel(in);
+        } else {
+            mAppDestination = null;
+        }
+        if (in.readBoolean()) {
+            mWebDestination = Uri.CREATOR.createFromParcel(in);
+        } else {
+            mWebDestination = null;
+        }
+        if (in.readBoolean()) {
+            mVerifiedDestination = Uri.CREATOR.createFromParcel(in);
+        } else {
+            mVerifiedDestination = null;
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof WebSourceRegistrationRequest)) return false;
+        WebSourceRegistrationRequest that = (WebSourceRegistrationRequest) o;
+        return Objects.equals(mWebSourceParams, that.mWebSourceParams)
+                && Objects.equals(mTopOriginUri, that.mTopOriginUri)
+                && Objects.equals(mInputEvent, that.mInputEvent)
+                && Objects.equals(mAppDestination, that.mAppDestination)
+                && Objects.equals(mWebDestination, that.mWebDestination)
+                && Objects.equals(mVerifiedDestination, that.mVerifiedDestination);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mWebSourceParams,
+                mTopOriginUri,
+                mInputEvent,
+                mAppDestination,
+                mWebDestination,
+                mVerifiedDestination);
+    }
+
+    /** Getter for source params. */
+    @NonNull
+    public List<WebSourceParams> getSourceParams() {
+        return mWebSourceParams;
+    }
+
+    /** Getter for top origin Uri. */
+    @NonNull
+    public Uri getTopOriginUri() {
+        return mTopOriginUri;
+    }
+
+    /** Getter for input event. */
+    @Nullable
+    public InputEvent getInputEvent() {
+        return mInputEvent;
+    }
+
+    /**
+     * Getter for the app destination. It is the android app {@link Uri} where corresponding
+     * conversion is expected. At least one of app destination or web destination is required.
+     */
+    @Nullable
+    public Uri getAppDestination() {
+        return mAppDestination;
+    }
+
+    /**
+     * Getter for web destination. It is the website {@link Uri} where corresponding conversion is
+     * expected. At least one of app destination or web destination is required.
+     */
+    @Nullable
+    public Uri getWebDestination() {
+        return mWebDestination;
+    }
+
+    /** Getter for verified destination. */
+    @Nullable
+    public Uri getVerifiedDestination() {
+        return mVerifiedDestination;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        Objects.requireNonNull(out);
+        out.writeList(mWebSourceParams);
+        mTopOriginUri.writeToParcel(out, flags);
+
+        if (mInputEvent != null) {
+            out.writeBoolean(true);
+            mInputEvent.writeToParcel(out, flags);
+        } else {
+            out.writeBoolean(false);
+        }
+        if (mAppDestination != null) {
+            out.writeBoolean(true);
+            mAppDestination.writeToParcel(out, flags);
+        } else {
+            out.writeBoolean(false);
+        }
+        if (mWebDestination != null) {
+            out.writeBoolean(true);
+            mWebDestination.writeToParcel(out, flags);
+        } else {
+            out.writeBoolean(false);
+        }
+        if (mVerifiedDestination != null) {
+            out.writeBoolean(true);
+            mVerifiedDestination.writeToParcel(out, flags);
+        } else {
+            out.writeBoolean(false);
+        }
+    }
+
+    /** Builder for {@link WebSourceRegistrationRequest}. */
+    public static final class Builder {
+        /** Registration info to fetch sources. */
+        @NonNull private final List<WebSourceParams> mWebSourceParams;
+        /** Top origin {@link Uri} of publisher. */
+        @NonNull private final Uri mTopOriginUri;
+        /**
+         * User Interaction InputEvent used by the AttributionReporting API to distinguish clicks
+         * from views.
+         */
+        @Nullable private InputEvent mInputEvent;
+        /**
+         * App destination of the source. It is the android app {@link Uri} where corresponding
+         * conversion is expected.
+         */
+        @Nullable private Uri mAppDestination;
+        /**
+         * Web destination of the source. It is the website {@link Uri} where corresponding
+         * conversion is expected.
+         */
+        @Nullable private Uri mWebDestination;
+        /**
+         * Verified destination by the caller. If available, sources should be checked against it.
+         */
+        @Nullable private Uri mVerifiedDestination;
+
+        /**
+         * Builder constructor for {@link WebSourceRegistrationRequest}.
+         *
+         * @param webSourceParams source parameters containing source registration parameters, the
+         *     list should not be empty
+         * @param topOriginUri source publisher {@link Uri}
+         */
+        public Builder(@NonNull List<WebSourceParams> webSourceParams, @NonNull Uri topOriginUri) {
+            Objects.requireNonNull(webSourceParams);
+            Objects.requireNonNull(topOriginUri);
+            if (webSourceParams.isEmpty() || webSourceParams.size() > WEB_SOURCE_PARAMS_MAX_COUNT) {
+                throw new IllegalArgumentException(
+                        "web source params size is not within bounds, size: "
+                                + webSourceParams.size());
+            }
+            mWebSourceParams = webSourceParams;
+            mTopOriginUri = topOriginUri;
+        }
+
+        /**
+         * Setter for input event.
+         *
+         * @param inputEvent User Interaction InputEvent used by the AttributionReporting API to
+         *     distinguish clicks from views.
+         * @return builder
+         */
+        @NonNull
+        public Builder setInputEvent(@Nullable InputEvent inputEvent) {
+            mInputEvent = inputEvent;
+            return this;
+        }
+
+        /**
+         * Setter for app destination. It is the android app {@link Uri} where corresponding
+         * conversion is expected. At least one of app destination or web destination is required.
+         *
+         * @param appDestination app destination {@link Uri}
+         * @return builder
+         */
+        @NonNull
+        public Builder setAppDestination(@Nullable Uri appDestination) {
+            if (appDestination != null) {
+                String scheme = appDestination.getScheme();
+                Uri destination;
+                if (scheme == null) {
+                    destination = Uri.parse(ANDROID_APP_SCHEME + "://" + appDestination);
+                } else if (!scheme.equals(ANDROID_APP_SCHEME)) {
+                    throw new IllegalArgumentException(
+                            String.format(
+                                    "appDestination scheme must be %s " + "or null. Received: %s",
+                                    ANDROID_APP_SCHEME, scheme));
+                } else {
+                    destination = appDestination;
+                }
+                mAppDestination = destination;
+            }
+            return this;
+        }
+
+        /**
+         * Setter for web destination. It is the website {@link Uri} where corresponding conversion
+         * is expected. At least one of app destination or web destination is required.
+         *
+         * @param webDestination web destination {@link Uri}
+         * @return builder
+         */
+        @NonNull
+        public Builder setWebDestination(@Nullable Uri webDestination) {
+            if (webDestination != null) {
+                validateScheme("Web destination", webDestination);
+                mWebDestination = webDestination;
+            }
+            return this;
+        }
+
+        /**
+         * Setter for verified destination.
+         *
+         * @param verifiedDestination verified destination
+         * @return builder
+         */
+        @NonNull
+        public Builder setVerifiedDestination(@Nullable Uri verifiedDestination) {
+            mVerifiedDestination = verifiedDestination;
+            return this;
+        }
+
+        /** Pre-validates parameters and builds {@link WebSourceRegistrationRequest}. */
+        @NonNull
+        public WebSourceRegistrationRequest build() {
+            return new WebSourceRegistrationRequest(this);
+        }
+    }
+
+    private static void validateScheme(String name, Uri uri) throws IllegalArgumentException {
+        if (uri.getScheme() == null) {
+            throw new IllegalArgumentException(name + " must have a scheme.");
+        }
+    }
+}
diff --git a/android-35/android/adservices/measurement/WebSourceRegistrationRequestInternal.java b/android-35/android/adservices/measurement/WebSourceRegistrationRequestInternal.java
new file mode 100644
index 0000000..1420520
--- /dev/null
+++ b/android-35/android/adservices/measurement/WebSourceRegistrationRequestInternal.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.measurement;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Internal source registration request object to communicate from {@link MeasurementManager} to
+ * {@link IMeasurementService}.
+ *
+ * @hide
+ */
+public class WebSourceRegistrationRequestInternal implements Parcelable {
+    /** Creator for Parcelable (via reflection). */
+    public static final Parcelable.Creator<WebSourceRegistrationRequestInternal> CREATOR =
+            new Parcelable.Creator<WebSourceRegistrationRequestInternal>() {
+                @Override
+                public WebSourceRegistrationRequestInternal createFromParcel(Parcel in) {
+                    return new WebSourceRegistrationRequestInternal(in);
+                }
+
+                @Override
+                public WebSourceRegistrationRequestInternal[] newArray(int size) {
+                    return new WebSourceRegistrationRequestInternal[size];
+                }
+            };
+    /** Holds input to measurement source registration calls from web context. */
+    @NonNull private final WebSourceRegistrationRequest mSourceRegistrationRequest;
+    /** Holds app package info of where the request is coming from. */
+    @NonNull private final String mAppPackageName;
+    /** Holds sdk package info of where the request is coming from. */
+    @NonNull private final String mSdkPackageName;
+    /** Time the request was created, as millis since boot excluding time in deep sleep. */
+    private final long mRequestTime;
+    /** AD ID Permission Granted. */
+    private final boolean mIsAdIdPermissionGranted;
+
+    private WebSourceRegistrationRequestInternal(@NonNull Builder builder) {
+        mSourceRegistrationRequest = builder.mSourceRegistrationRequest;
+        mAppPackageName = builder.mAppPackageName;
+        mSdkPackageName = builder.mSdkPackageName;
+        mRequestTime = builder.mRequestTime;
+        mIsAdIdPermissionGranted = builder.mIsAdIdPermissionGranted;
+    }
+
+    private WebSourceRegistrationRequestInternal(Parcel in) {
+        Objects.requireNonNull(in);
+        mSourceRegistrationRequest = WebSourceRegistrationRequest.CREATOR.createFromParcel(in);
+        mAppPackageName = in.readString();
+        mSdkPackageName = in.readString();
+        mRequestTime = in.readLong();
+        mIsAdIdPermissionGranted = in.readBoolean();
+    }
+
+    /** Getter for {@link #mSourceRegistrationRequest}. */
+    public WebSourceRegistrationRequest getSourceRegistrationRequest() {
+        return mSourceRegistrationRequest;
+    }
+
+    /** Getter for {@link #mAppPackageName}. */
+    public String getAppPackageName() {
+        return mAppPackageName;
+    }
+
+    /** Getter for {@link #mSdkPackageName}. */
+    public String getSdkPackageName() {
+        return mSdkPackageName;
+    }
+
+    /** Getter for {@link #mRequestTime}. */
+    public long getRequestTime() {
+        return mRequestTime;
+    }
+
+    /** Getter for {@link #mIsAdIdPermissionGranted}. */
+    public boolean isAdIdPermissionGranted() {
+        return mIsAdIdPermissionGranted;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof WebSourceRegistrationRequestInternal)) return false;
+        WebSourceRegistrationRequestInternal that = (WebSourceRegistrationRequestInternal) o;
+        return Objects.equals(mSourceRegistrationRequest, that.mSourceRegistrationRequest)
+                && Objects.equals(mAppPackageName, that.mAppPackageName)
+                && Objects.equals(mSdkPackageName, that.mSdkPackageName)
+                && mRequestTime == that.mRequestTime
+                && mIsAdIdPermissionGranted == that.mIsAdIdPermissionGranted;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mSourceRegistrationRequest,
+                mAppPackageName,
+                mSdkPackageName,
+                mIsAdIdPermissionGranted);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        Objects.requireNonNull(out);
+        mSourceRegistrationRequest.writeToParcel(out, flags);
+        out.writeString(mAppPackageName);
+        out.writeString(mSdkPackageName);
+        out.writeLong(mRequestTime);
+        out.writeBoolean(mIsAdIdPermissionGranted);
+    }
+
+    /** Builder for {@link WebSourceRegistrationRequestInternal}. */
+    public static final class Builder {
+        /** External source registration request from client app SDK. */
+        @NonNull private final WebSourceRegistrationRequest mSourceRegistrationRequest;
+        /** Package name of the app used for the registration. Used to determine the registrant. */
+        @NonNull private final String mAppPackageName;
+        /** Package name of the sdk used for the registration. */
+        @NonNull private final String mSdkPackageName;
+        /** Time the request was created, as millis since boot excluding time in deep sleep. */
+        private final long mRequestTime;
+        /** AD ID Permission Granted. */
+        private boolean mIsAdIdPermissionGranted;
+        /**
+         * Builder constructor for {@link WebSourceRegistrationRequestInternal}.
+         *
+         * @param sourceRegistrationRequest external source registration request
+         * @param appPackageName app package name that is calling PP API
+         * @param sdkPackageName sdk package name that is calling PP API
+         */
+        public Builder(
+                @NonNull WebSourceRegistrationRequest sourceRegistrationRequest,
+                @NonNull String appPackageName,
+                @NonNull String sdkPackageName,
+                long requestTime) {
+            Objects.requireNonNull(sourceRegistrationRequest);
+            Objects.requireNonNull(appPackageName);
+            Objects.requireNonNull(sdkPackageName);
+            mSourceRegistrationRequest = sourceRegistrationRequest;
+            mAppPackageName = appPackageName;
+            mSdkPackageName = sdkPackageName;
+            mRequestTime = requestTime;
+        }
+
+        /** Pre-validates parameters and builds {@link WebSourceRegistrationRequestInternal}. */
+        @NonNull
+        public WebSourceRegistrationRequestInternal build() {
+            return new WebSourceRegistrationRequestInternal(this);
+        }
+
+        /** See {@link WebSourceRegistrationRequestInternal#isAdIdPermissionGranted()}. */
+        public WebSourceRegistrationRequestInternal.Builder setAdIdPermissionGranted(
+                boolean isAdIdPermissionGranted) {
+            mIsAdIdPermissionGranted = isAdIdPermissionGranted;
+            return this;
+        }
+    }
+}
diff --git a/android-35/android/adservices/measurement/WebTriggerParams.java b/android-35/android/adservices/measurement/WebTriggerParams.java
new file mode 100644
index 0000000..fc501c2
--- /dev/null
+++ b/android-35/android/adservices/measurement/WebTriggerParams.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.measurement;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/** Class holding trigger registration parameters. */
+public final class WebTriggerParams implements Parcelable {
+    /** Creator for Paracelable (via reflection). */
+    @NonNull
+    public static final Creator<WebTriggerParams> CREATOR =
+            new Creator<WebTriggerParams>() {
+                @Override
+                public WebTriggerParams createFromParcel(Parcel in) {
+                    return new WebTriggerParams(in);
+                }
+
+                @Override
+                public WebTriggerParams[] newArray(int size) {
+                    return new WebTriggerParams[size];
+                }
+            };
+    /**
+     * URI that the Attribution Reporting API sends a request to in order to obtain trigger
+     * registration parameters.
+     */
+    @NonNull private final Uri mRegistrationUri;
+    /**
+     * Used by the browser to indicate whether the debug key obtained from the registration URI is
+     * allowed to be used.
+     */
+    private final boolean mDebugKeyAllowed;
+
+    private WebTriggerParams(@NonNull Builder builder) {
+        mRegistrationUri = builder.mRegistrationUri;
+        mDebugKeyAllowed = builder.mDebugKeyAllowed;
+    }
+
+    /** Unpack a TriggerRegistration from a Parcel. */
+    private WebTriggerParams(@NonNull Parcel in) {
+        mRegistrationUri = Uri.CREATOR.createFromParcel(in);
+        mDebugKeyAllowed = in.readBoolean();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof WebTriggerParams)) return false;
+        WebTriggerParams that = (WebTriggerParams) o;
+        return mDebugKeyAllowed == that.mDebugKeyAllowed
+                && Objects.equals(mRegistrationUri, that.mRegistrationUri);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRegistrationUri, mDebugKeyAllowed);
+    }
+
+    /** Getter for registration Uri. */
+    @NonNull
+    public Uri getRegistrationUri() {
+        return mRegistrationUri;
+    }
+
+    /**
+     * Getter for debug allowed/disallowed flag. Its value as {@code true} means to allow parsing
+     * debug keys from registration responses and their addition in the generated reports.
+     */
+    public boolean isDebugKeyAllowed() {
+        return mDebugKeyAllowed;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        Objects.requireNonNull(out);
+        mRegistrationUri.writeToParcel(out, flags);
+        out.writeBoolean(mDebugKeyAllowed);
+    }
+
+    /** A builder for {@link WebTriggerParams}. */
+    public static final class Builder {
+        /**
+         * URI that the Attribution Reporting API sends a request to in order to obtain trigger
+         * registration parameters.
+         */
+        @NonNull private final Uri mRegistrationUri;
+        /**
+         * Used by the browser to indicate whether the debug key obtained from the registration URI
+         * is allowed to be used.
+         */
+        private boolean mDebugKeyAllowed;
+
+        /**
+         * Builder constructor for {@link WebTriggerParams}. {@code mIsDebugKeyAllowed} is assigned
+         * false by default.
+         *
+         * @param registrationUri URI that the Attribution Reporting API sends a request to in order
+         *     to obtain trigger registration parameters
+         * @throws IllegalArgumentException if the scheme for {@code registrationUri} is not HTTPS
+         */
+        public Builder(@NonNull Uri registrationUri) {
+            Objects.requireNonNull(registrationUri);
+            if (registrationUri.getScheme() == null
+                    || !registrationUri.getScheme().equalsIgnoreCase("https")) {
+                throw new IllegalArgumentException("registrationUri must have an HTTPS scheme");
+            }
+            mRegistrationUri = registrationUri;
+            mDebugKeyAllowed = false;
+        }
+
+        /**
+         * Setter for debug allow/disallow flag. Setting it to true will allow parsing debug keys
+         * from registration responses and their addition in the generated reports.
+         *
+         * @param debugKeyAllowed used by the browser to indicate whether the debug key obtained
+         *     from the registration URI is allowed to be used
+         * @return builder
+         */
+        @NonNull
+        public Builder setDebugKeyAllowed(boolean debugKeyAllowed) {
+            mDebugKeyAllowed = debugKeyAllowed;
+            return this;
+        }
+
+        /**
+         * Builds immutable {@link WebTriggerParams}.
+         *
+         * @return immutable {@link WebTriggerParams}
+         */
+        @NonNull
+        public WebTriggerParams build() {
+            return new WebTriggerParams(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/measurement/WebTriggerRegistrationRequest.java b/android-35/android/adservices/measurement/WebTriggerRegistrationRequest.java
new file mode 100644
index 0000000..a7a70d4
--- /dev/null
+++ b/android-35/android/adservices/measurement/WebTriggerRegistrationRequest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.measurement;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/** Class to hold input to measurement trigger registration calls from web context. */
+public final class WebTriggerRegistrationRequest implements Parcelable {
+    private static final int WEB_TRIGGER_PARAMS_MAX_COUNT = 80;
+
+    /** Creator for Paracelable (via reflection). */
+    @NonNull
+    public static final Parcelable.Creator<WebTriggerRegistrationRequest> CREATOR =
+            new Parcelable.Creator<WebTriggerRegistrationRequest>() {
+                @Override
+                public WebTriggerRegistrationRequest createFromParcel(Parcel in) {
+                    return new WebTriggerRegistrationRequest(in);
+                }
+
+                @Override
+                public WebTriggerRegistrationRequest[] newArray(int size) {
+                    return new WebTriggerRegistrationRequest[size];
+                }
+            };
+    /** Registration info to fetch sources. */
+    @NonNull private final List<WebTriggerParams> mWebTriggerParams;
+
+    /** Destination {@link Uri}. */
+    @NonNull private final Uri mDestination;
+
+    private WebTriggerRegistrationRequest(@NonNull Builder builder) {
+        mWebTriggerParams = builder.mWebTriggerParams;
+        mDestination = builder.mDestination;
+    }
+
+    private WebTriggerRegistrationRequest(Parcel in) {
+        Objects.requireNonNull(in);
+        ArrayList<WebTriggerParams> webTriggerParams = new ArrayList<>();
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+            in.readList(webTriggerParams, WebTriggerParams.class.getClassLoader());
+        } else {
+            in.readList(
+                    webTriggerParams,
+                    WebTriggerParams.class.getClassLoader(),
+                    WebTriggerParams.class);
+        }
+        mWebTriggerParams = webTriggerParams;
+        mDestination = Uri.CREATOR.createFromParcel(in);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof WebTriggerRegistrationRequest)) return false;
+        WebTriggerRegistrationRequest that = (WebTriggerRegistrationRequest) o;
+        return Objects.equals(mWebTriggerParams, that.mWebTriggerParams)
+                && Objects.equals(mDestination, that.mDestination);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mWebTriggerParams, mDestination);
+    }
+
+    /** Getter for trigger params. */
+    @NonNull
+    public List<WebTriggerParams> getTriggerParams() {
+        return mWebTriggerParams;
+    }
+
+    /** Getter for destination. */
+    @NonNull
+    public Uri getDestination() {
+        return mDestination;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        Objects.requireNonNull(out);
+        out.writeList(mWebTriggerParams);
+        mDestination.writeToParcel(out, flags);
+    }
+
+    /** Builder for {@link WebTriggerRegistrationRequest}. */
+    public static final class Builder {
+        /**
+         * Registration info to fetch triggers. Maximum 80 registrations allowed at once, to be in
+         * sync with Chrome platform.
+         */
+        @NonNull private List<WebTriggerParams> mWebTriggerParams;
+        /** Top level origin of publisher app. */
+        @NonNull private final Uri mDestination;
+
+        /**
+         * Builder constructor for {@link WebTriggerRegistrationRequest}.
+         *
+         * @param webTriggerParams contains trigger registration parameters, the list should not be
+         *     empty
+         * @param destination trigger destination {@link Uri}
+         */
+        public Builder(@NonNull List<WebTriggerParams> webTriggerParams, @NonNull Uri destination) {
+            Objects.requireNonNull(webTriggerParams);
+            if (webTriggerParams.isEmpty()
+                    || webTriggerParams.size() > WEB_TRIGGER_PARAMS_MAX_COUNT) {
+                throw new IllegalArgumentException(
+                        "web trigger params size is not within bounds, size: "
+                                + webTriggerParams.size());
+            }
+
+            Objects.requireNonNull(destination);
+            if (destination.getScheme() == null) {
+                throw new IllegalArgumentException("Destination origin must have a scheme.");
+            }
+            mWebTriggerParams = webTriggerParams;
+            mDestination = destination;
+
+        }
+
+        /** Pre-validates parameters and builds {@link WebTriggerRegistrationRequest}. */
+        @NonNull
+        public WebTriggerRegistrationRequest build() {
+            return new WebTriggerRegistrationRequest(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/measurement/WebTriggerRegistrationRequestInternal.java b/android-35/android/adservices/measurement/WebTriggerRegistrationRequestInternal.java
new file mode 100644
index 0000000..b92d54c
--- /dev/null
+++ b/android-35/android/adservices/measurement/WebTriggerRegistrationRequestInternal.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.measurement;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Internal trigger registration request object to communicate from {@link MeasurementManager} to
+ * {@link IMeasurementService}.
+ *
+ * @hide
+ */
+public class WebTriggerRegistrationRequestInternal implements Parcelable {
+    /** Creator for Parcelable (via reflection). */
+    @NonNull
+    public static final Creator<WebTriggerRegistrationRequestInternal> CREATOR =
+            new Creator<WebTriggerRegistrationRequestInternal>() {
+                @Override
+                public WebTriggerRegistrationRequestInternal createFromParcel(Parcel in) {
+                    return new WebTriggerRegistrationRequestInternal(in);
+                }
+
+                @Override
+                public WebTriggerRegistrationRequestInternal[] newArray(int size) {
+                    return new WebTriggerRegistrationRequestInternal[size];
+                }
+            };
+    /** Holds input to measurement trigger registration calls from web context. */
+    @NonNull private final WebTriggerRegistrationRequest mTriggerRegistrationRequest;
+    /** Holds app package info of where the request is coming from. */
+    @NonNull private final String mAppPackageName;
+    /** Holds sdk package info of where the request is coming from. */
+    @NonNull private final String mSdkPackageName;
+    /** AD ID Permission Granted. */
+    private final boolean mIsAdIdPermissionGranted;
+
+    private WebTriggerRegistrationRequestInternal(@NonNull Builder builder) {
+        mTriggerRegistrationRequest = builder.mTriggerRegistrationRequest;
+        mAppPackageName = builder.mAppPackageName;
+        mSdkPackageName = builder.mSdkPackageName;
+        mIsAdIdPermissionGranted = builder.mIsAdIdPermissionGranted;
+    }
+
+    private WebTriggerRegistrationRequestInternal(Parcel in) {
+        Objects.requireNonNull(in);
+        mTriggerRegistrationRequest = WebTriggerRegistrationRequest.CREATOR.createFromParcel(in);
+        mAppPackageName = in.readString();
+        mSdkPackageName = in.readString();
+        mIsAdIdPermissionGranted = in.readBoolean();
+    }
+
+    /** Getter for {@link #mTriggerRegistrationRequest}. */
+    public WebTriggerRegistrationRequest getTriggerRegistrationRequest() {
+        return mTriggerRegistrationRequest;
+    }
+
+    /** Getter for {@link #mAppPackageName}. */
+    public String getAppPackageName() {
+        return mAppPackageName;
+    }
+
+    /** Getter for {@link #mSdkPackageName}. */
+    public String getSdkPackageName() {
+        return mSdkPackageName;
+    }
+
+    /** Getter for {@link #mIsAdIdPermissionGranted}. */
+    public boolean isAdIdPermissionGranted() {
+        return mIsAdIdPermissionGranted;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof WebTriggerRegistrationRequestInternal)) return false;
+        WebTriggerRegistrationRequestInternal that = (WebTriggerRegistrationRequestInternal) o;
+        return Objects.equals(mTriggerRegistrationRequest, that.mTriggerRegistrationRequest)
+                && Objects.equals(mAppPackageName, that.mAppPackageName)
+                && Objects.equals(mSdkPackageName, that.mSdkPackageName)
+                && Objects.equals(mIsAdIdPermissionGranted, that.mIsAdIdPermissionGranted);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mTriggerRegistrationRequest,
+                mAppPackageName,
+                mSdkPackageName,
+                mIsAdIdPermissionGranted);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        Objects.requireNonNull(out);
+        mTriggerRegistrationRequest.writeToParcel(out, flags);
+        out.writeString(mAppPackageName);
+        out.writeString(mSdkPackageName);
+        out.writeBoolean(mIsAdIdPermissionGranted);
+    }
+
+    /** Builder for {@link WebTriggerRegistrationRequestInternal}. */
+    public static final class Builder {
+        /** External trigger registration request from client app SDK. */
+        @NonNull private final WebTriggerRegistrationRequest mTriggerRegistrationRequest;
+        /** Package name of the app used for the registration. Used to determine the registrant. */
+        @NonNull private final String mAppPackageName;
+        /** Package name of the sdk used for the registration. */
+        @NonNull private final String mSdkPackageName;
+        /** AD ID Permission Granted. */
+        private boolean mIsAdIdPermissionGranted;
+
+        /**
+         * Builder constructor for {@link WebTriggerRegistrationRequestInternal}.
+         *
+         * @param triggerRegistrationRequest external trigger registration request
+         * @param appPackageName app package name that is calling PP API
+         * @param sdkPackageName sdk package name that is calling PP API
+         */
+        public Builder(
+                @NonNull WebTriggerRegistrationRequest triggerRegistrationRequest,
+                @NonNull String appPackageName,
+                @NonNull String sdkPackageName) {
+            Objects.requireNonNull(triggerRegistrationRequest);
+            Objects.requireNonNull(appPackageName);
+            Objects.requireNonNull(sdkPackageName);
+            mTriggerRegistrationRequest = triggerRegistrationRequest;
+            mAppPackageName = appPackageName;
+            mSdkPackageName = sdkPackageName;
+        }
+
+        /** Pre-validates parameters and builds {@link WebTriggerRegistrationRequestInternal}. */
+        @NonNull
+        public WebTriggerRegistrationRequestInternal build() {
+            return new WebTriggerRegistrationRequestInternal(this);
+        }
+
+        /** See {@link WebTriggerRegistrationRequestInternal#isAdIdPermissionGranted()}. */
+        public WebTriggerRegistrationRequestInternal.Builder setAdIdPermissionGranted(
+                boolean isAdIdPermissionGranted) {
+            mIsAdIdPermissionGranted = isAdIdPermissionGranted;
+            return this;
+        }
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/AppInfo.java b/android-35/android/adservices/ondevicepersonalization/AppInfo.java
new file mode 100644
index 0000000..57ce7f9
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/AppInfo.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * Information about apps.
+ *
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genHiddenBuilder = true, genEqualsHashCode = true)
+public final class AppInfo implements Parcelable {
+    /** Whether the app is installed. */
+    @NonNull boolean mInstalled = false;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/AppInfo.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ AppInfo(
+            @NonNull boolean installed) {
+        this.mInstalled = installed;
+        AnnotationValidations.validate(
+                NonNull.class, null, mInstalled);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Whether the app is installed.
+     */
+    @DataClass.Generated.Member
+    public @NonNull boolean isInstalled() {
+        return mInstalled;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(AppInfo other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        AppInfo that = (AppInfo) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && mInstalled == that.mInstalled;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + Boolean.hashCode(mInstalled);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mInstalled) flg |= 0x1;
+        dest.writeByte(flg);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ AppInfo(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        boolean installed = (flg & 0x1) != 0;
+
+        this.mInstalled = installed;
+        AnnotationValidations.validate(
+                NonNull.class, null, mInstalled);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<AppInfo> CREATOR
+            = new Parcelable.Creator<AppInfo>() {
+        @Override
+        public AppInfo[] newArray(int size) {
+            return new AppInfo[size];
+        }
+
+        @Override
+        public AppInfo createFromParcel(@NonNull android.os.Parcel in) {
+            return new AppInfo(in);
+        }
+    };
+
+    /**
+     * A builder for {@link AppInfo}
+     * @hide
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @NonNull boolean mInstalled;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * Whether the app is installed.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setInstalled(@NonNull boolean value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mInstalled = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull AppInfo build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mInstalled = false;
+            }
+            AppInfo o = new AppInfo(
+                    mInstalled);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x2) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1695492606666L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/AppInfo.java",
+            inputSignatures = " @android.annotation.NonNull boolean mInstalled\nclass AppInfo extends java.lang.Object implements [android.os.Parcelable]\[email protected](genHiddenBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/CalleeMetadata.java b/android-35/android/adservices/ondevicepersonalization/CalleeMetadata.java
new file mode 100644
index 0000000..f5ad7a0
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/CalleeMetadata.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * Wrapper class for additional information returned with IPC results.
+*
+* @hide
+*/
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public final class CalleeMetadata implements Parcelable {
+    /** Time elapsed in callee, as measured by callee. */
+    private long mElapsedTimeMillis = 0;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/CalleeMetadata.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ CalleeMetadata(
+            long elapsedTimeMillis) {
+        this.mElapsedTimeMillis = elapsedTimeMillis;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Time elapsed in callee, as measured by callee.
+     */
+    @DataClass.Generated.Member
+    public long getElapsedTimeMillis() {
+        return mElapsedTimeMillis;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(CalleeMetadata other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        CalleeMetadata that = (CalleeMetadata) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && mElapsedTimeMillis == that.mElapsedTimeMillis;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + Long.hashCode(mElapsedTimeMillis);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeLong(mElapsedTimeMillis);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ CalleeMetadata(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        long elapsedTimeMillis = in.readLong();
+
+        this.mElapsedTimeMillis = elapsedTimeMillis;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<CalleeMetadata> CREATOR
+            = new Parcelable.Creator<CalleeMetadata>() {
+        @Override
+        public CalleeMetadata[] newArray(int size) {
+            return new CalleeMetadata[size];
+        }
+
+        @Override
+        public CalleeMetadata createFromParcel(@NonNull android.os.Parcel in) {
+            return new CalleeMetadata(in);
+        }
+    };
+
+    /**
+     * A builder for {@link CalleeMetadata}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private long mElapsedTimeMillis;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * Time elapsed in callee, as measured by callee.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setElapsedTimeMillis(long value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mElapsedTimeMillis = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull CalleeMetadata build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mElapsedTimeMillis = 0;
+            }
+            CalleeMetadata o = new CalleeMetadata(
+                    mElapsedTimeMillis);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x2) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1696885546254L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/CalleeMetadata.java",
+            inputSignatures = "private  long mElapsedTimeMillis\nclass CalleeMetadata extends java.lang.Object implements [android.os.Parcelable]\[email protected](genBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/CallerMetadata.java b/android-35/android/adservices/ondevicepersonalization/CallerMetadata.java
new file mode 100644
index 0000000..68a9cd8
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/CallerMetadata.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * Wrapper class for additional information passed to IPC requests.
+*
+* @hide
+*/
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public final class CallerMetadata implements Parcelable {
+    /** Start time of the operation. */
+    private long mStartTimeMillis = 0;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/CallerMetadata.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ CallerMetadata(
+            long startTimeMillis) {
+        this.mStartTimeMillis = startTimeMillis;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Start time of the operation.
+     */
+    @DataClass.Generated.Member
+    public long getStartTimeMillis() {
+        return mStartTimeMillis;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(CallerMetadata other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        CallerMetadata that = (CallerMetadata) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && mStartTimeMillis == that.mStartTimeMillis;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + Long.hashCode(mStartTimeMillis);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeLong(mStartTimeMillis);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ CallerMetadata(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        long startTimeMillis = in.readLong();
+
+        this.mStartTimeMillis = startTimeMillis;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<CallerMetadata> CREATOR
+            = new Parcelable.Creator<CallerMetadata>() {
+        @Override
+        public CallerMetadata[] newArray(int size) {
+            return new CallerMetadata[size];
+        }
+
+        @Override
+        public CallerMetadata createFromParcel(@NonNull android.os.Parcel in) {
+            return new CallerMetadata(in);
+        }
+    };
+
+    /**
+     * A builder for {@link CallerMetadata}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private long mStartTimeMillis;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * Start time of the operation.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setStartTimeMillis(long value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mStartTimeMillis = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull CallerMetadata build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mStartTimeMillis = 0;
+            }
+            CallerMetadata o = new CallerMetadata(
+                    mStartTimeMillis);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x2) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1696884555838L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/CallerMetadata.java",
+            inputSignatures = "private  long mStartTimeMillis\nclass CallerMetadata extends java.lang.Object implements [android.os.Parcelable]\[email protected](genBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/Constants.java b/android-35/android/adservices/ondevicepersonalization/Constants.java
new file mode 100644
index 0000000..c7c1e45
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/Constants.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+/**
+ * Constants used internally in the OnDevicePersonalization Module and not used in public APIs.
+ *
+ * @hide
+ */
+public class Constants {
+    // Status codes used within the ODP service or returned from service to manager classes.
+    // These will be mapped to existing platform exceptions or subclasses of
+    // OnDevicePersonalizationException in APIs.
+    public static final int STATUS_SUCCESS = 0;
+    public static final int STATUS_INTERNAL_ERROR = 100;
+    public static final int STATUS_NAME_NOT_FOUND = 101;
+    public static final int STATUS_CLASS_NOT_FOUND = 102;
+    public static final int STATUS_SERVICE_FAILED = 103;
+    public static final int STATUS_PERSONALIZATION_DISABLED = 104;
+    public static final int STATUS_KEY_NOT_FOUND = 105;
+
+    // Operations implemented by IsolatedService.
+    public static final int OP_EXECUTE = 1;
+    public static final int OP_DOWNLOAD = 2;
+    public static final int OP_RENDER = 3;
+    public static final int OP_WEB_VIEW_EVENT = 4;
+    public static final int OP_TRAINING_EXAMPLE = 5;
+    public static final int OP_WEB_TRIGGER = 6;
+
+    // Keys for Bundle objects passed between processes.
+    public static final String EXTRA_APP_PARAMS_SERIALIZED =
+            "android.ondevicepersonalization.extra.app_params_serialized";
+    public static final String EXTRA_CALLEE_METADATA =
+            "android.ondevicepersonalization.extra.callee_metadata";
+    public static final String EXTRA_DATA_ACCESS_SERVICE_BINDER =
+            "android.ondevicepersonalization.extra.data_access_service_binder";
+    public static final String EXTRA_FEDERATED_COMPUTE_SERVICE_BINDER =
+            "android.ondevicepersonalization.extra.federated_computation_service_binder";
+    public static final String EXTRA_MODEL_SERVICE_BINDER =
+            "android.ondevicepersonalization.extra.model_service_binder";
+    public static final String EXTRA_DESTINATION_URL =
+            "android.ondevicepersonalization.extra.destination_url";
+    public static final String EXTRA_EVENT_PARAMS =
+            "android.ondevicepersonalization.extra.event_params";
+    public static final String EXTRA_INPUT = "android.ondevicepersonalization.extra.input";
+    public static final String EXTRA_LOOKUP_KEYS =
+            "android.ondevicepersonalization.extra.lookup_keys";
+    public static final String EXTRA_MEASUREMENT_WEB_TRIGGER_PARAMS =
+            "android.adservices.ondevicepersonalization.measurement_web_trigger_params";
+    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_MODEL_INPUTS =
+            "android.ondevicepersonalization.extra.model_inputs";
+    public static final String EXTRA_MODEL_OUTPUTS =
+            "android.ondevicepersonalization.extra.model_outputs";
+    // Inference related constants,
+    public static final String EXTRA_INFERENCE_INPUT =
+            "android.ondevicepersonalization.extra.inference_input";
+    public static final String EXTRA_MODEL_ID = "android.ondevicepersonalization.extra.model_id";
+
+    // API Names for API metrics logging. Must match the values in
+    // frameworks/proto_logging/stats/atoms/ondevicepersonalization/ondevicepersonalization_extension_atoms.proto
+    public static final int API_NAME_UNKNOWN = 0;
+    public static final int API_NAME_EXECUTE = 1;
+    public static final int API_NAME_REQUEST_SURFACE_PACKAGE = 2;
+    public static final int API_NAME_SERVICE_ON_EXECUTE = 3;
+    public static final int API_NAME_SERVICE_ON_DOWNLOAD_COMPLETED = 4;
+    public static final int API_NAME_SERVICE_ON_RENDER = 5;
+    public static final int API_NAME_SERVICE_ON_EVENT = 6;
+    public static final int API_NAME_SERVICE_ON_TRAINING_EXAMPLE = 7;
+    public static final int API_NAME_SERVICE_ON_WEB_TRIGGER = 8;
+    public static final int API_NAME_REMOTE_DATA_GET = 9;
+    public static final int API_NAME_REMOTE_DATA_KEYSET = 10;
+    public static final int API_NAME_LOCAL_DATA_GET = 11;
+    public static final int API_NAME_LOCAL_DATA_KEYSET = 12;
+    public static final int API_NAME_LOCAL_DATA_PUT = 13;
+    public static final int API_NAME_LOCAL_DATA_REMOVE = 14;
+    public static final int API_NAME_EVENT_URL_CREATE_WITH_RESPONSE = 15;
+    public static final int API_NAME_EVENT_URL_CREATE_WITH_REDIRECT = 16;
+    public static final int API_NAME_LOG_READER_GET_REQUESTS = 17;
+    public static final int API_NAME_LOG_READER_GET_JOINED_EVENTS = 18;
+    public static final int API_NAME_FEDERATED_COMPUTE_SCHEDULE = 19;
+    public static final int API_NAME_FEDERATED_COMPUTE_CANCEL = 21;
+    public static final int API_NAME_MODEL_MANAGER_RUN = 20;
+
+    // Data Access Service operations.
+    public static final int DATA_ACCESS_OP_REMOTE_DATA_LOOKUP = 1;
+    public static final int DATA_ACCESS_OP_REMOTE_DATA_KEYSET = 2;
+    public static final int DATA_ACCESS_OP_GET_EVENT_URL = 3;
+    public static final int DATA_ACCESS_OP_LOCAL_DATA_LOOKUP = 4;
+    public static final int DATA_ACCESS_OP_LOCAL_DATA_KEYSET = 5;
+    public static final int DATA_ACCESS_OP_LOCAL_DATA_PUT = 6;
+    public static final int DATA_ACCESS_OP_LOCAL_DATA_REMOVE = 7;
+    public static final int DATA_ACCESS_OP_GET_REQUESTS = 8;
+    public static final int DATA_ACCESS_OP_GET_JOINED_EVENTS = 9;
+    public static final int DATA_ACCESS_OP_GET_MODEL = 10;
+
+    // Measurement event types for measurement events received from the OS.
+    public static final int MEASUREMENT_EVENT_TYPE_WEB_TRIGGER = 1;
+
+    private Constants() {}
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/DownloadCompletedInput.java b/android-35/android/adservices/ondevicepersonalization/DownloadCompletedInput.java
new file mode 100644
index 0000000..147d2da
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/DownloadCompletedInput.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * The input data for {@link
+ * IsolatedWorker#onDownloadCompleted(DownloadCompletedInput, android.os.OutcomeReceiver)}.
+ *
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genHiddenBuilder = true, genEqualsHashCode = true)
+public final class DownloadCompletedInput {
+    /**
+     * A {@link KeyValueStore} that contains the downloaded content.
+     */
+    @NonNull KeyValueStore mDownloadedContents;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/DownloadCompletedInput.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ DownloadCompletedInput(
+            @NonNull KeyValueStore downloadedContents) {
+        this.mDownloadedContents = downloadedContents;
+        AnnotationValidations.validate(
+                NonNull.class, null, mDownloadedContents);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Map containing downloaded keys and values
+     */
+    @DataClass.Generated.Member
+    public @NonNull KeyValueStore getDownloadedContents() {
+        return mDownloadedContents;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(DownloadCompletedInput other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        DownloadCompletedInput that = (DownloadCompletedInput) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mDownloadedContents, that.mDownloadedContents);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mDownloadedContents);
+        return _hash;
+    }
+
+    /**
+     * A builder for {@link DownloadCompletedInput}
+     * @hide
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @NonNull KeyValueStore mDownloadedContents;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * Creates a new Builder.
+         *
+         * @param downloadedContents
+         *   Map containing downloaded keys and values
+         */
+        public Builder(
+                @NonNull KeyValueStore downloadedContents) {
+            mDownloadedContents = downloadedContents;
+            AnnotationValidations.validate(
+                    NonNull.class, null, mDownloadedContents);
+        }
+
+        /**
+         * Map containing downloaded keys and values
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setDownloadedContents(@NonNull KeyValueStore value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mDownloadedContents = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull DownloadCompletedInput build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2; // Mark builder used
+
+            DownloadCompletedInput o = new DownloadCompletedInput(
+                    mDownloadedContents);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x2) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1706205792643L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/DownloadCompletedInput.java",
+            inputSignatures = " @android.annotation.NonNull android.adservices.ondevicepersonalization.KeyValueStore mDownloadedContents\nclass DownloadCompletedInput extends java.lang.Object implements []\[email protected](genHiddenBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/DownloadCompletedOutput.java b/android-35/android/adservices/ondevicepersonalization/DownloadCompletedOutput.java
new file mode 100644
index 0000000..538e17c
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/DownloadCompletedOutput.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The result returned by {@link
+ * IsolatedWorker#onDownloadCompleted(DownloadCompletedInput, android.os.OutcomeReceiver)}.
+ *
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public final class DownloadCompletedOutput {
+    /**
+     * The keys to be retained in the REMOTE_DATA table. Any existing keys that are not
+     * present in this list are removed from the table.
+     */
+    @DataClass.PluralOf("retainedKey")
+    @NonNull private List<String> mRetainedKeys = Collections.emptyList();
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/DownloadCompletedOutput.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ DownloadCompletedOutput(
+            @NonNull List<String> retainedKeys) {
+        this.mRetainedKeys = retainedKeys;
+        AnnotationValidations.validate(
+                NonNull.class, null, mRetainedKeys);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The keys to be retained in the REMOTE_DATA table. Any existing keys that are not
+     * present in this list are removed from the table.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<String> getRetainedKeys() {
+        return mRetainedKeys;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(DownloadCompletedOutput other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        DownloadCompletedOutput that = (DownloadCompletedOutput) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mRetainedKeys, that.mRetainedKeys);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mRetainedKeys);
+        return _hash;
+    }
+
+    /**
+     * A builder for {@link DownloadCompletedOutput}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @NonNull List<String> mRetainedKeys;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * The keys to be retained in the REMOTE_DATA table. Any existing keys that are not
+         * present in this list are removed from the table.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setRetainedKeys(@NonNull List<String> value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mRetainedKeys = value;
+            return this;
+        }
+
+        /** @see #setRetainedKeys */
+        @DataClass.Generated.Member
+        public @NonNull Builder addRetainedKey(@NonNull String value) {
+            if (mRetainedKeys == null) setRetainedKeys(new java.util.ArrayList<>());
+            mRetainedKeys.add(value);
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull DownloadCompletedOutput build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mRetainedKeys = Collections.emptyList();
+            }
+            DownloadCompletedOutput o = new DownloadCompletedOutput(
+                    mRetainedKeys);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x2) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1698862918590L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/DownloadCompletedOutput.java",
+            inputSignatures = "private @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"retainedKey\") @android.annotation.NonNull java.util.List<java.lang.String> mRetainedKeys\nclass DownloadCompletedOutput extends java.lang.Object implements []\[email protected](genBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/DownloadCompletedOutputParcel.java b/android-35/android/adservices/ondevicepersonalization/DownloadCompletedOutputParcel.java
new file mode 100644
index 0000000..7a906f1
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/DownloadCompletedOutputParcel.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Parcelable version of {@link DownloadCompletedOutput}.
+ * @hide
+ */
+@DataClass(genAidl = false, genBuilder =  false)
+public final class DownloadCompletedOutputParcel implements Parcelable {
+    /**
+     * The keys to be retained in the REMOTE_DATA table. Any existing keys that are not
+     * present in this list are removed from the table.
+     */
+    @NonNull private List<String> mRetainedKeys = Collections.emptyList();
+
+    /** @hide */
+    public DownloadCompletedOutputParcel(@NonNull DownloadCompletedOutput value) {
+        this(value.getRetainedKeys());
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/DownloadCompletedOutputParcel.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new DownloadCompletedOutputParcel.
+     *
+     * @param retainedKeys
+     *   The keys to be retained in the REMOTE_DATA table. Any existing keys that are not
+     *   present in this list are removed from the table.
+     */
+    @DataClass.Generated.Member
+    public DownloadCompletedOutputParcel(
+            @NonNull List<String> retainedKeys) {
+        this.mRetainedKeys = retainedKeys;
+        AnnotationValidations.validate(
+                NonNull.class, null, mRetainedKeys);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The keys to be retained in the REMOTE_DATA table. Any existing keys that are not
+     * present in this list are removed from the table.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<String> getRetainedKeys() {
+        return mRetainedKeys;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeStringList(mRetainedKeys);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ DownloadCompletedOutputParcel(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        List<String> retainedKeys = new java.util.ArrayList<>();
+        in.readStringList(retainedKeys);
+
+        this.mRetainedKeys = retainedKeys;
+        AnnotationValidations.validate(
+                NonNull.class, null, mRetainedKeys);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<DownloadCompletedOutputParcel> CREATOR
+            = new Parcelable.Creator<DownloadCompletedOutputParcel>() {
+        @Override
+        public DownloadCompletedOutputParcel[] newArray(int size) {
+            return new DownloadCompletedOutputParcel[size];
+        }
+
+        @Override
+        public DownloadCompletedOutputParcel createFromParcel(@NonNull android.os.Parcel in) {
+            return new DownloadCompletedOutputParcel(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1698783477713L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/DownloadCompletedOutputParcel.java",
+            inputSignatures = "private @android.annotation.NonNull java.util.List<java.lang.String> mRetainedKeys\nclass DownloadCompletedOutputParcel extends java.lang.Object implements [android.os.Parcelable]\[email protected](genAidl=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/DownloadInputParcel.java b/android-35/android/adservices/ondevicepersonalization/DownloadInputParcel.java
new file mode 100644
index 0000000..df8ce75
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/DownloadInputParcel.java
@@ -0,0 +1,197 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * The input sent to the {@link IsolatedService}.
+ *
+ * @hide
+ */
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public class DownloadInputParcel implements Parcelable {
+    /** DataAccessService binder for downloaded content */
+    @Nullable
+    IBinder mDataAccessServiceBinder = null;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/DownloadInputParcel.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ DownloadInputParcel(
+            @Nullable IBinder dataAccessServiceBinder) {
+        this.mDataAccessServiceBinder = dataAccessServiceBinder;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * DataAccessService binder for downloaded content
+     */
+    @DataClass.Generated.Member
+    public @Nullable IBinder getDataAccessServiceBinder() {
+        return mDataAccessServiceBinder;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(DownloadInputParcel other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        DownloadInputParcel that = (DownloadInputParcel) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mDataAccessServiceBinder, that.mDataAccessServiceBinder);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mDataAccessServiceBinder);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mDataAccessServiceBinder != null) flg |= 0x1;
+        dest.writeByte(flg);
+        if (mDataAccessServiceBinder != null) dest.writeStrongBinder(mDataAccessServiceBinder);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected DownloadInputParcel(@android.annotation.NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        IBinder dataAccessServiceBinder = (flg & 0x1) == 0 ? null : (IBinder) in.readStrongBinder();
+
+        this.mDataAccessServiceBinder = dataAccessServiceBinder;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @android.annotation.NonNull Parcelable.Creator<DownloadInputParcel> CREATOR
+            = new Parcelable.Creator<DownloadInputParcel>() {
+        @Override
+        public DownloadInputParcel[] newArray(int size) {
+            return new DownloadInputParcel[size];
+        }
+
+        @Override
+        public DownloadInputParcel createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
+            return new DownloadInputParcel(in);
+        }
+    };
+
+    /**
+     * A builder for {@link DownloadInputParcel}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static class Builder {
+
+        private @Nullable IBinder mDataAccessServiceBinder;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * DataAccessService binder for downloaded content
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setDataAccessServiceBinder(@android.annotation.NonNull IBinder value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mDataAccessServiceBinder = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @android.annotation.NonNull DownloadInputParcel build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mDataAccessServiceBinder = null;
+            }
+            DownloadInputParcel o = new DownloadInputParcel(
+                    mDataAccessServiceBinder);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x2) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1705968510939L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/DownloadInputParcel.java",
+            inputSignatures = " @android.annotation.Nullable android.adservices.ondevicepersonalization.IBinder mDataAccessServiceBinder\nclass DownloadInputParcel extends java.lang.Object implements [android.os.Parcelable]\[email protected](genBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/EventInput.java b/android-35/android/adservices/ondevicepersonalization/EventInput.java
new file mode 100644
index 0000000..0ee3f67
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/EventInput.java
@@ -0,0 +1,152 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.PersistableBundle;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * The input data for {@link
+ * IsolatedWorker#onEvent(EventInput, android.os.OutcomeReceiver)}.
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = false, genHiddenConstructor = true, genEqualsHashCode = true)
+public final class EventInput {
+    /**
+     * The {@link RequestLogRecord} that was returned as a result of
+     * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}.
+     */
+    @Nullable private RequestLogRecord mRequestLogRecord = null;
+
+    /**
+     * The Event URL parameters that the service passed to {@link
+     * EventUrlProvider#createEventTrackingUrlWithResponse(PersistableBundle, byte[], String)}
+     * or {@link EventUrlProvider#createEventTrackingUrlWithRedirect(PersistableBundle, Uri)}.
+     */
+    @NonNull private PersistableBundle mParameters = PersistableBundle.EMPTY;
+
+    /** @hide */
+    public EventInput(@NonNull EventInputParcel parcel) {
+        this(parcel.getRequestLogRecord(), parcel.getParameters());
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/EventInput.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new EventInput.
+     *
+     * @param requestLogRecord
+     *   The {@link RequestLogRecord} that was returned as a result of
+     *   {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}.
+     * @param parameters
+     *   The Event URL parameters that the service passed to {@link
+     *   EventUrlProvider#createEventTrackingUrlWithResponse(PersistableBundle, byte[], String)}
+     *   or {@link EventUrlProvider#createEventTrackingUrlWithRedirect(PersistableBundle, Uri)}.
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public EventInput(
+            @Nullable RequestLogRecord requestLogRecord,
+            @NonNull PersistableBundle parameters) {
+        this.mRequestLogRecord = requestLogRecord;
+        this.mParameters = parameters;
+        AnnotationValidations.validate(
+                NonNull.class, null, mParameters);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The {@link RequestLogRecord} that was returned as a result of
+     * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}.
+     */
+    @DataClass.Generated.Member
+    public @Nullable RequestLogRecord getRequestLogRecord() {
+        return mRequestLogRecord;
+    }
+
+    /**
+     * The Event URL parameters that the service passed to {@link
+     * EventUrlProvider#createEventTrackingUrlWithResponse(PersistableBundle, byte[], String)}
+     * or {@link EventUrlProvider#createEventTrackingUrlWithRedirect(PersistableBundle, Uri)}.
+     */
+    @DataClass.Generated.Member
+    public @NonNull PersistableBundle getParameters() {
+        return mParameters;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(EventInput other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        EventInput that = (EventInput) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mRequestLogRecord, that.mRequestLogRecord)
+                && java.util.Objects.equals(mParameters, that.mParameters);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mRequestLogRecord);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mParameters);
+        return _hash;
+    }
+
+    @DataClass.Generated(
+            time = 1698882321696L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/EventInput.java",
+            inputSignatures = "private @android.annotation.Nullable android.adservices.ondevicepersonalization.RequestLogRecord mRequestLogRecord\nprivate @android.annotation.NonNull android.os.PersistableBundle mParameters\nclass EventInput extends java.lang.Object implements []\[email protected](genBuilder=false, genHiddenConstructor=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/EventInputParcel.java b/android-35/android/adservices/ondevicepersonalization/EventInputParcel.java
new file mode 100644
index 0000000..a94bd66
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/EventInputParcel.java
@@ -0,0 +1,220 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * Parcelable version of {@link EventInput}.
+ * @hide
+ */
+@DataClass(genAidl = false, genHiddenBuilder = true)
+public final class EventInputParcel implements Parcelable {
+    /**
+     * The {@link RequestLogRecord} that was returned as a result of
+     * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}.
+     */
+    @Nullable private RequestLogRecord mRequestLogRecord = null;
+
+    /**
+     * The Event URL parameters that the service passed to {@link
+     * EventUrlProvider#createEventTrackingUrlWithResponse(PersistableBundle, byte[], String)}
+     * or {@link EventUrlProvider#createEventTrackingUrlWithRedirect(PersistableBundle, Uri)}.
+     */
+    @NonNull private PersistableBundle mParameters = PersistableBundle.EMPTY;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/EventInputParcel.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ EventInputParcel(
+            @Nullable RequestLogRecord requestLogRecord,
+            @NonNull PersistableBundle parameters) {
+        this.mRequestLogRecord = requestLogRecord;
+        this.mParameters = parameters;
+        AnnotationValidations.validate(
+                NonNull.class, null, mParameters);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The {@link RequestLogRecord} that was returned as a result of
+     * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}.
+     */
+    @DataClass.Generated.Member
+    public @Nullable RequestLogRecord getRequestLogRecord() {
+        return mRequestLogRecord;
+    }
+
+    /**
+     * The Event URL parameters that the service passed to {@link
+     * EventUrlProvider#createEventTrackingUrlWithResponse(PersistableBundle, byte[], String)}
+     * or {@link EventUrlProvider#createEventTrackingUrlWithRedirect(PersistableBundle, Uri)}.
+     */
+    @DataClass.Generated.Member
+    public @NonNull PersistableBundle getParameters() {
+        return mParameters;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mRequestLogRecord != null) flg |= 0x1;
+        dest.writeByte(flg);
+        if (mRequestLogRecord != null) dest.writeTypedObject(mRequestLogRecord, flags);
+        dest.writeTypedObject(mParameters, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ EventInputParcel(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        RequestLogRecord requestLogRecord = (flg & 0x1) == 0 ? null : (RequestLogRecord) in.readTypedObject(RequestLogRecord.CREATOR);
+        PersistableBundle parameters = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR);
+
+        this.mRequestLogRecord = requestLogRecord;
+        this.mParameters = parameters;
+        AnnotationValidations.validate(
+                NonNull.class, null, mParameters);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<EventInputParcel> CREATOR
+            = new Parcelable.Creator<EventInputParcel>() {
+        @Override
+        public EventInputParcel[] newArray(int size) {
+            return new EventInputParcel[size];
+        }
+
+        @Override
+        public EventInputParcel createFromParcel(@NonNull android.os.Parcel in) {
+            return new EventInputParcel(in);
+        }
+    };
+
+    /**
+     * A builder for {@link EventInputParcel}
+     * @hide
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @Nullable RequestLogRecord mRequestLogRecord;
+        private @NonNull PersistableBundle mParameters;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * The {@link RequestLogRecord} that was returned as a result of
+         * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setRequestLogRecord(@NonNull RequestLogRecord value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mRequestLogRecord = value;
+            return this;
+        }
+
+        /**
+         * The Event URL parameters that the service passed to {@link
+         * EventUrlProvider#createEventTrackingUrlWithResponse(PersistableBundle, byte[], String)}
+         * or {@link EventUrlProvider#createEventTrackingUrlWithRedirect(PersistableBundle, Uri)}.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setParameters(@NonNull PersistableBundle value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mParameters = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull EventInputParcel build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mRequestLogRecord = null;
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mParameters = PersistableBundle.EMPTY;
+            }
+            EventInputParcel o = new EventInputParcel(
+                    mRequestLogRecord,
+                    mParameters);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x4) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1698875208124L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/EventInputParcel.java",
+            inputSignatures = "private @android.annotation.Nullable android.adservices.ondevicepersonalization.RequestLogRecord mRequestLogRecord\nprivate @android.annotation.NonNull android.os.PersistableBundle mParameters\nclass EventInputParcel extends java.lang.Object implements [android.os.Parcelable]\[email protected](genAidl=false, genHiddenBuilder=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/EventLogRecord.java b/android-35/android/adservices/ondevicepersonalization/EventLogRecord.java
new file mode 100644
index 0000000..54ab70c
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/EventLogRecord.java
@@ -0,0 +1,432 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentValues;
+import android.os.Parcelable;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+import java.time.Instant;
+
+// TODO(b/289102463): Add a link to the public doc for the EVENTS table when available.
+/**
+ * Data to be logged in the EVENTS table.
+ *
+ * Each record in the EVENTS table is associated with one row from an existing
+ * {@link RequestLogRecord} in the requests table {@link RequestLogRecord#getRows()}.
+ * The purpose of the EVENTS table is to add supplemental information to logged data
+ * from a prior request, e.g., logging an event when a link in a rendered WebView is
+ * clicked {@code IsolatedWorker#onEvent(EventInput, android.os.OutcomeReceiver)}.
+ * The contents of the EVENTS table can be
+ * consumed by Federated Learning facilitated model training, or Federated Analytics facilitated
+ * cross-device statistical analysis.
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public final class EventLogRecord implements Parcelable {
+    /**
+     * The index of the row in an existing {@link RequestLogRecord} that this payload should be
+     * associated with.
+     **/
+    private @IntRange(from = 0) int mRowIndex = 0;
+
+    /**
+     * The service-assigned identifier that identifies this payload. Each row in
+     * {@link RequestLogRecord} can be associated with up to one event of a specified type.
+     * The platform drops events if another event with the same type already exists for a row
+     * in {@link RequestLogRecord}. Must be >0 and <128. This allows up to 127 events to be
+     * written for each row in {@link RequestLogRecord}. If unspecified, the default is 1.
+     */
+    private @IntRange(from = 1, to = 127) int mType = 1;
+
+    /**
+     * Time of the event in milliseconds.
+     * @hide
+     */
+    private long mTimeMillis = 0;
+
+    /**
+     * Additional data to be logged. Can be null if no additional data needs to be written as part
+     * of the event, and only the occurrence of the event needs to be logged.
+     */
+    @DataClass.MaySetToNull
+    @Nullable ContentValues mData = null;
+
+    /**
+     * The existing {@link RequestLogRecord} that this payload should be associated with. In an
+     * implementation of
+     * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}, this should be
+     * set to a value returned by {@link LogReader#getRequests(Instant, Instant)}. In an
+     * implementation of {@link IsolatedWorker#onEvent(EventInput, android.os.OutcomeReceiver)},
+     * this should be set to {@code null} because the payload will be automatically associated with
+     * the current {@link RequestLogRecord}.
+     *
+     */
+    @DataClass.MaySetToNull
+    @Nullable RequestLogRecord mRequestLogRecord = null;
+
+    /**
+     * Returns the timestamp of this record.
+     */
+    @NonNull public Instant getTime() {
+        return Instant.ofEpochMilli(getTimeMillis());
+    }
+
+    abstract static class BaseBuilder {
+        /**
+         * @hide
+         */
+        public abstract Builder setTimeMillis(long value);
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/EventLogRecord.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ EventLogRecord(
+            @IntRange(from = 0) int rowIndex,
+            @IntRange(from = 1, to = 127) int type,
+            long timeMillis,
+            @Nullable ContentValues data,
+            @Nullable RequestLogRecord requestLogRecord) {
+        this.mRowIndex = rowIndex;
+        AnnotationValidations.validate(
+                IntRange.class, null, mRowIndex,
+                "from", 0);
+        this.mType = type;
+        AnnotationValidations.validate(
+                IntRange.class, null, mType,
+                "from", 1,
+                "to", 127);
+        this.mTimeMillis = timeMillis;
+        this.mData = data;
+        this.mRequestLogRecord = requestLogRecord;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The index of the row in an existing {@link RequestLogRecord} that this payload should be
+     * associated with.
+     */
+    @DataClass.Generated.Member
+    public @IntRange(from = 0) int getRowIndex() {
+        return mRowIndex;
+    }
+
+    /**
+     * The service-assigned identifier that identifies this payload. Each row in
+     * {@link RequestLogRecord} can be associated with up to one event of a specified type.
+     * The platform drops events if another event with the same type already exists for a row
+     * in {@link RequestLogRecord}. Must be >0 and <128. This allows up to 127 events to be
+     * written for each row in {@link RequestLogRecord}. If unspecified, the default is 1.
+     */
+    @DataClass.Generated.Member
+    public @IntRange(from = 1, to = 127) int getType() {
+        return mType;
+    }
+
+    /**
+     * Time of the event in milliseconds.
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public long getTimeMillis() {
+        return mTimeMillis;
+    }
+
+    /**
+     * Additional data to be logged. Can be null if no additional data needs to be written as part
+     * of the event, and only the occurrence of the event needs to be logged.
+     */
+    @DataClass.Generated.Member
+    public @Nullable ContentValues getData() {
+        return mData;
+    }
+
+    /**
+     * The existing {@link RequestLogRecord} that this payload should be associated with. In an
+     * implementation of
+     * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}, this should be
+     * set to a value returned by {@link LogReader#getRequests(Instant, Instant)}. In an
+     * implementation of {@link IsolatedWorker#onEvent(EventInput, android.os.OutcomeReceiver)},
+     * this should be set to {@code null} because the payload will be automatically associated with
+     * the current {@link RequestLogRecord}.
+     */
+    @DataClass.Generated.Member
+    public @Nullable RequestLogRecord getRequestLogRecord() {
+        return mRequestLogRecord;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(EventLogRecord other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        EventLogRecord that = (EventLogRecord) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && mRowIndex == that.mRowIndex
+                && mType == that.mType
+                && mTimeMillis == that.mTimeMillis
+                && java.util.Objects.equals(mData, that.mData)
+                && java.util.Objects.equals(mRequestLogRecord, that.mRequestLogRecord);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + mRowIndex;
+        _hash = 31 * _hash + mType;
+        _hash = 31 * _hash + Long.hashCode(mTimeMillis);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mData);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mRequestLogRecord);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mData != null) flg |= 0x8;
+        if (mRequestLogRecord != null) flg |= 0x10;
+        dest.writeByte(flg);
+        dest.writeInt(mRowIndex);
+        dest.writeInt(mType);
+        dest.writeLong(mTimeMillis);
+        if (mData != null) dest.writeTypedObject(mData, flags);
+        if (mRequestLogRecord != null) dest.writeTypedObject(mRequestLogRecord, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ EventLogRecord(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        int rowIndex = in.readInt();
+        int type = in.readInt();
+        long timeMillis = in.readLong();
+        ContentValues data = (flg & 0x8) == 0 ? null : (ContentValues) in.readTypedObject(ContentValues.CREATOR);
+        RequestLogRecord requestLogRecord = (flg & 0x10) == 0 ? null : (RequestLogRecord) in.readTypedObject(RequestLogRecord.CREATOR);
+
+        this.mRowIndex = rowIndex;
+        AnnotationValidations.validate(
+                IntRange.class, null, mRowIndex,
+                "from", 0);
+        this.mType = type;
+        AnnotationValidations.validate(
+                IntRange.class, null, mType,
+                "from", 1,
+                "to", 127);
+        this.mTimeMillis = timeMillis;
+        this.mData = data;
+        this.mRequestLogRecord = requestLogRecord;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<EventLogRecord> CREATOR
+            = new Parcelable.Creator<EventLogRecord>() {
+        @Override
+        public EventLogRecord[] newArray(int size) {
+            return new EventLogRecord[size];
+        }
+
+        @Override
+        public EventLogRecord createFromParcel(@NonNull android.os.Parcel in) {
+            return new EventLogRecord(in);
+        }
+    };
+
+    /**
+     * A builder for {@link EventLogRecord}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder extends BaseBuilder {
+
+        private @IntRange(from = 0) int mRowIndex;
+        private @IntRange(from = 1, to = 127) int mType;
+        private long mTimeMillis;
+        private @Nullable ContentValues mData;
+        private @Nullable RequestLogRecord mRequestLogRecord;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * The index of the row in an existing {@link RequestLogRecord} that this payload should be
+         * associated with.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setRowIndex(@IntRange(from = 0) int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mRowIndex = value;
+            return this;
+        }
+
+        /**
+         * The service-assigned identifier that identifies this payload. Each row in
+         * {@link RequestLogRecord} can be associated with up to one event of a specified type.
+         * The platform drops events if another event with the same type already exists for a row
+         * in {@link RequestLogRecord}. Must be >0 and <128. This allows up to 127 events to be
+         * written for each row in {@link RequestLogRecord}. If unspecified, the default is 1.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setType(@IntRange(from = 1, to = 127) int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mType = value;
+            return this;
+        }
+
+        /**
+         * Time of the event in milliseconds.
+         *
+         * @hide
+         */
+        @DataClass.Generated.Member
+        @Override
+        public @NonNull Builder setTimeMillis(long value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mTimeMillis = value;
+            return this;
+        }
+
+        /**
+         * Additional data to be logged. Can be null if no additional data needs to be written as part
+         * of the event, and only the occurrence of the event needs to be logged.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setData(@Nullable ContentValues value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mData = value;
+            return this;
+        }
+
+        /**
+         * The existing {@link RequestLogRecord} that this payload should be associated with. In an
+         * implementation of
+         * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}, this should be
+         * set to a value returned by {@link LogReader#getRequests(Instant, Instant)}. In an
+         * implementation of {@link IsolatedWorker#onEvent(EventInput, android.os.OutcomeReceiver)},
+         * this should be set to {@code null} because the payload will be automatically associated with
+         * the current {@link RequestLogRecord}.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setRequestLogRecord(@Nullable RequestLogRecord value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10;
+            mRequestLogRecord = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull EventLogRecord build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x20; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mRowIndex = 0;
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mType = 1;
+            }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mTimeMillis = 0;
+            }
+            if ((mBuilderFieldsSet & 0x8) == 0) {
+                mData = null;
+            }
+            if ((mBuilderFieldsSet & 0x10) == 0) {
+                mRequestLogRecord = null;
+            }
+            EventLogRecord o = new EventLogRecord(
+                    mRowIndex,
+                    mType,
+                    mTimeMillis,
+                    mData,
+                    mRequestLogRecord);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x20) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1707253467187L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/EventLogRecord.java",
+            inputSignatures = "private @android.annotation.IntRange int mRowIndex\nprivate @android.annotation.IntRange int mType\nprivate  long mTimeMillis\n @com.android.ondevicepersonalization.internal.util.DataClass.MaySetToNull @android.annotation.Nullable android.content.ContentValues mData\n @com.android.ondevicepersonalization.internal.util.DataClass.MaySetToNull @android.annotation.Nullable android.adservices.ondevicepersonalization.RequestLogRecord mRequestLogRecord\npublic @android.annotation.NonNull java.time.Instant getTime()\nclass EventLogRecord extends java.lang.Object implements [android.os.Parcelable]\npublic abstract  android.adservices.ondevicepersonalization.EventLogRecord.Builder setTimeMillis(long)\nclass BaseBuilder extends java.lang.Object implements []\[email protected](genBuilder=true, genEqualsHashCode=true)\npublic abstract  android.adservices.ondevicepersonalization.EventLogRecord.Builder setTimeMillis(long)\nclass BaseBuilder extends java.lang.Object implements []")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/EventOutput.java b/android-35/android/adservices/ondevicepersonalization/EventOutput.java
new file mode 100644
index 0000000..d44031e
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/EventOutput.java
@@ -0,0 +1,159 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.Nullable;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ *  The result returned by {@link IsolatedWorker#onEvent(EventInput, android.os.OutcomeReceiver)}.
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public final class EventOutput {
+    /**
+     * An {@link EventLogRecord} to be written to the EVENTS table, if not null. Each
+     * {@link EventLogRecord} is associated with a row in an existing {@link RequestLogRecord} that
+     * has been written to the REQUESTS table.
+     */
+    @DataClass.MaySetToNull
+    @Nullable EventLogRecord mEventLogRecord = null;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/EventOutput.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ EventOutput(
+            @Nullable EventLogRecord eventLogRecord) {
+        this.mEventLogRecord = eventLogRecord;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * An {@link EventLogRecord} to be written to the EVENTS table, if not null. Each
+     * {@link EventLogRecord} is associated with a row in an existing {@link RequestLogRecord} that
+     * has been written to the REQUESTS table.
+     */
+    @DataClass.Generated.Member
+    public @Nullable EventLogRecord getEventLogRecord() {
+        return mEventLogRecord;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(EventOutput other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        EventOutput that = (EventOutput) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mEventLogRecord, that.mEventLogRecord);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mEventLogRecord);
+        return _hash;
+    }
+
+    /**
+     * A builder for {@link EventOutput}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @Nullable EventLogRecord mEventLogRecord;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * An {@link EventLogRecord} to be written to the EVENTS table, if not null. Each
+         * {@link EventLogRecord} is associated with a row in an existing {@link RequestLogRecord} that
+         * has been written to the REQUESTS table.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setEventLogRecord(@Nullable EventLogRecord value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mEventLogRecord = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @android.annotation.NonNull EventOutput build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mEventLogRecord = null;
+            }
+            EventOutput o = new EventOutput(
+                    mEventLogRecord);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x2) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1707253681044L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/EventOutput.java",
+            inputSignatures = " @com.android.ondevicepersonalization.internal.util.DataClass.MaySetToNull @android.annotation.Nullable android.adservices.ondevicepersonalization.EventLogRecord mEventLogRecord\nclass EventOutput extends java.lang.Object implements []\[email protected](genBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/EventOutputParcel.java b/android-35/android/adservices/ondevicepersonalization/EventOutputParcel.java
new file mode 100644
index 0000000..5432a6c
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/EventOutputParcel.java
@@ -0,0 +1,141 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * Parcelable version of {@link EventOutput}.
+ * @hide
+ */
+@DataClass(genAidl = false, genBuilder = false)
+public final class EventOutputParcel implements Parcelable {
+    /**
+     * An {@link EventLogRecord} to be written to the EVENTS table, if not null. Each
+     * {@link EventLogRecord} is associated with a row in an existing {@link RequestLogRecord} that
+     * has been written to the REQUESTS table.
+     */
+    @Nullable EventLogRecord mEventLogRecord = null;
+
+    /** @hide */
+    public EventOutputParcel(@NonNull EventOutput value) {
+        this(value.getEventLogRecord());
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/EventOutputParcel.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new EventOutputParcel.
+     *
+     * @param eventLogRecord
+     *   An {@link EventLogRecord} to be written to the EVENTS table, if not null. Each
+     *   {@link EventLogRecord} is associated with a row in an existing {@link RequestLogRecord} that
+     *   has been written to the REQUESTS table.
+     */
+    @DataClass.Generated.Member
+    public EventOutputParcel(
+            @Nullable EventLogRecord eventLogRecord) {
+        this.mEventLogRecord = eventLogRecord;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * An {@link EventLogRecord} to be written to the EVENTS table, if not null. Each
+     * {@link EventLogRecord} is associated with a row in an existing {@link RequestLogRecord} that
+     * has been written to the REQUESTS table.
+     */
+    @DataClass.Generated.Member
+    public @Nullable EventLogRecord getEventLogRecord() {
+        return mEventLogRecord;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mEventLogRecord != null) flg |= 0x1;
+        dest.writeByte(flg);
+        if (mEventLogRecord != null) dest.writeTypedObject(mEventLogRecord, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ EventOutputParcel(@android.annotation.NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        EventLogRecord eventLogRecord = (flg & 0x1) == 0 ? null : (EventLogRecord) in.readTypedObject(EventLogRecord.CREATOR);
+
+        this.mEventLogRecord = eventLogRecord;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @android.annotation.NonNull Parcelable.Creator<EventOutputParcel> CREATOR
+            = new Parcelable.Creator<EventOutputParcel>() {
+        @Override
+        public EventOutputParcel[] newArray(int size) {
+            return new EventOutputParcel[size];
+        }
+
+        @Override
+        public EventOutputParcel createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
+            return new EventOutputParcel(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1698864082503L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/EventOutputParcel.java",
+            inputSignatures = " @android.annotation.Nullable android.adservices.ondevicepersonalization.EventLogRecord mEventLogRecord\nclass EventOutputParcel extends java.lang.Object implements [android.os.Parcelable]\[email protected](genAidl=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/EventUrlProvider.java b/android-35/android/adservices/ondevicepersonalization/EventUrlProvider.java
new file mode 100644
index 0000000..5abd17b
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/EventUrlProvider.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.adservices.ondevicepersonalization.aidl.IDataAccessService;
+import android.adservices.ondevicepersonalization.aidl.IDataAccessServiceCallback;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.LoggerFactory;
+
+import java.util.Objects;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+/**
+ * Generates event tracking URLs for a request. The service can embed these URLs within the
+ * HTML output as needed. When the HTML is rendered within an ODP WebView, ODP will intercept
+ * requests to these URLs, call
+ * {@code IsolatedWorker#onEvent(EventInput, android.os.OutcomeReceiver)}, and log the returned
+ * output in the EVENTS table.
+ *
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public class EventUrlProvider {
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
+    private static final String TAG = EventUrlProvider.class.getSimpleName();
+    private static final long ASYNC_TIMEOUT_MS = 1000;
+
+    @NonNull private final IDataAccessService mDataAccessService;
+
+    /** @hide */
+    public EventUrlProvider(@NonNull IDataAccessService binder) {
+        mDataAccessService = Objects.requireNonNull(binder);
+    }
+
+    /**
+     * Creates an event tracking URL that returns the provided response. Returns HTTP Status
+     * 200 (OK) if the response data is not empty. Returns HTTP Status 204 (No Content) if the
+     * response data is empty.
+     *
+     * @param eventParams The data to be passed to
+     *     {@code IsolatedWorker#onEvent(EventInput, android.os.OutcomeReceiver)}
+     *     when the event occurs.
+     * @param responseData The content to be returned to the WebView when the URL is fetched.
+     * @param mimeType The Mime Type of the URL response.
+     * @return An ODP event URL that can be inserted into a WebView.
+     */
+    @WorkerThread
+    @NonNull public Uri createEventTrackingUrlWithResponse(
+            @NonNull PersistableBundle eventParams,
+            @Nullable byte[] responseData,
+            @Nullable String mimeType) {
+        final long startTimeMillis = System.currentTimeMillis();
+        Bundle params = new Bundle();
+        params.putParcelable(Constants.EXTRA_EVENT_PARAMS, eventParams);
+        params.putByteArray(Constants.EXTRA_RESPONSE_DATA, responseData);
+        params.putString(Constants.EXTRA_MIME_TYPE, mimeType);
+        return getUrl(params, Constants.API_NAME_EVENT_URL_CREATE_WITH_RESPONSE, startTimeMillis);
+    }
+
+    /**
+     * Creates an event tracking URL that redirects to the provided destination URL when it is
+     * clicked in an ODP webview.
+     *
+     * @param eventParams The data to be passed to
+     *     {@code IsolatedWorker#onEvent(EventInput, android.os.OutcomeReceiver)}
+     *     when the event occurs
+     * @param destinationUrl The URL to redirect to.
+     * @return An ODP event URL that can be inserted into a WebView.
+     */
+    @WorkerThread
+    @NonNull public Uri createEventTrackingUrlWithRedirect(
+            @NonNull PersistableBundle eventParams,
+            @Nullable Uri destinationUrl) {
+        final long startTimeMillis = System.currentTimeMillis();
+        Bundle params = new Bundle();
+        params.putParcelable(Constants.EXTRA_EVENT_PARAMS, eventParams);
+        params.putString(Constants.EXTRA_DESTINATION_URL, destinationUrl.toString());
+        return getUrl(params, Constants.API_NAME_EVENT_URL_CREATE_WITH_REDIRECT, startTimeMillis);
+    }
+
+    @NonNull private Uri getUrl(
+            @NonNull Bundle params, int apiName, long startTimeMillis) {
+        int responseCode = Constants.STATUS_SUCCESS;
+        try {
+            BlockingQueue<CallbackResult> asyncResult = new ArrayBlockingQueue<>(1);
+
+            mDataAccessService.onRequest(
+                    Constants.DATA_ACCESS_OP_GET_EVENT_URL,
+                    params,
+                    new IDataAccessServiceCallback.Stub() {
+                        @Override
+                        public void onSuccess(@NonNull Bundle result) {
+                            asyncResult.add(new CallbackResult(result, 0));
+                        }
+                        @Override
+                        public void onError(int errorCode) {
+                            asyncResult.add(new CallbackResult(null, errorCode));
+                        }
+                });
+            CallbackResult callbackResult = asyncResult.take();
+            Objects.requireNonNull(callbackResult);
+            if (callbackResult.mErrorCode != 0) {
+                throw new IllegalStateException("Error: " + callbackResult.mErrorCode);
+            }
+            Bundle result = Objects.requireNonNull(callbackResult.mResult);
+            Uri url = Objects.requireNonNull(
+                    result.getParcelable(Constants.EXTRA_RESULT, Uri.class));
+            return url;
+        } catch (InterruptedException | RemoteException e) {
+            responseCode = Constants.STATUS_INTERNAL_ERROR;
+            throw new RuntimeException(e);
+        } finally {
+            try {
+                mDataAccessService.logApiCallStats(
+                        apiName,
+                        System.currentTimeMillis() - startTimeMillis,
+                        responseCode);
+            } catch (Exception e) {
+                sLogger.d(e, TAG + ": failed to log metrics");
+            }
+        }
+    }
+
+    private static class CallbackResult {
+        final Bundle mResult;
+        final int mErrorCode;
+
+        CallbackResult(Bundle result, int errorCode) {
+            mResult = result;
+            mErrorCode = errorCode;
+        }
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/ExecuteInput.java b/android-35/android/adservices/ondevicepersonalization/ExecuteInput.java
new file mode 100644
index 0000000..cda9262
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/ExecuteInput.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.PersistableBundle;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.ByteArrayParceledSlice;
+import com.android.ondevicepersonalization.internal.util.PersistableBundleUtils;
+
+import java.util.Objects;
+
+/**
+ * The input data for {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}.
+ *
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public final class ExecuteInput {
+    @NonNull private final String mAppPackageName;
+    @Nullable private final ByteArrayParceledSlice mSerializedAppParams;
+    @NonNull private final Object mAppParamsLock = new Object();
+    @NonNull private volatile PersistableBundle mAppParams = null;
+
+    /** @hide */
+    public ExecuteInput(@NonNull ExecuteInputParcel parcel) {
+        mAppPackageName = Objects.requireNonNull(parcel.getAppPackageName());
+        mSerializedAppParams = parcel.getSerializedAppParams();
+    }
+
+    /**
+     * The package name of the calling app.
+     */
+    @NonNull public String getAppPackageName() {
+        return mAppPackageName;
+    }
+
+    /**
+     * The parameters provided by the app to the {@link IsolatedService}. The service
+     * defines the expected keys in this {@link PersistableBundle}.
+     */
+    @NonNull public PersistableBundle getAppParams() {
+        if (mAppParams != null) {
+            return mAppParams;
+        }
+        synchronized (mAppParamsLock) {
+            if (mAppParams != null) {
+                return mAppParams;
+            }
+            try {
+                mAppParams = (mSerializedAppParams != null)
+                        ? PersistableBundleUtils.fromByteArray(
+                                mSerializedAppParams.getByteArray())
+                        : PersistableBundle.EMPTY;
+                return mAppParams;
+            } catch (Exception e) {
+                throw new IllegalStateException(e);
+            }
+        }
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/ExecuteInputParcel.java b/android-35/android/adservices/ondevicepersonalization/ExecuteInputParcel.java
new file mode 100644
index 0000000..2058c13
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/ExecuteInputParcel.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.ByteArrayParceledSlice;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * Parcelable version of {@link ExecuteInput}.
+ * @hide
+ */
+@DataClass(genAidl = false, genHiddenBuilder = true)
+public final class ExecuteInputParcel implements Parcelable {
+    /**
+     * The package name of the calling app.
+     */
+    @NonNull String mAppPackageName = "";
+
+    /**
+     * Serialized app params.
+     * @hide
+     */
+    @Nullable ByteArrayParceledSlice mSerializedAppParams = null;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/ExecuteInputParcel.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ ExecuteInputParcel(
+            @NonNull String appPackageName,
+            @Nullable ByteArrayParceledSlice serializedAppParams) {
+        this.mAppPackageName = appPackageName;
+        AnnotationValidations.validate(
+                NonNull.class, null, mAppPackageName);
+        this.mSerializedAppParams = serializedAppParams;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The package name of the calling app.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getAppPackageName() {
+        return mAppPackageName;
+    }
+
+    /**
+     * Serialized app params.
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public @Nullable ByteArrayParceledSlice getSerializedAppParams() {
+        return mSerializedAppParams;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mSerializedAppParams != null) flg |= 0x2;
+        dest.writeByte(flg);
+        dest.writeString(mAppPackageName);
+        if (mSerializedAppParams != null) dest.writeTypedObject(mSerializedAppParams, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ ExecuteInputParcel(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        String appPackageName = in.readString();
+        ByteArrayParceledSlice serializedAppParams = (flg & 0x2) == 0 ? null : (ByteArrayParceledSlice) in.readTypedObject(ByteArrayParceledSlice.CREATOR);
+
+        this.mAppPackageName = appPackageName;
+        AnnotationValidations.validate(
+                NonNull.class, null, mAppPackageName);
+        this.mSerializedAppParams = serializedAppParams;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ExecuteInputParcel> CREATOR
+            = new Parcelable.Creator<ExecuteInputParcel>() {
+        @Override
+        public ExecuteInputParcel[] newArray(int size) {
+            return new ExecuteInputParcel[size];
+        }
+
+        @Override
+        public ExecuteInputParcel createFromParcel(@NonNull android.os.Parcel in) {
+            return new ExecuteInputParcel(in);
+        }
+    };
+
+    /**
+     * A builder for {@link ExecuteInputParcel}
+     * @hide
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @NonNull String mAppPackageName;
+        private @Nullable ByteArrayParceledSlice mSerializedAppParams;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * The package name of the calling app.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setAppPackageName(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mAppPackageName = value;
+            return this;
+        }
+
+        /**
+         * Serialized app params.
+         *
+         * @hide
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setSerializedAppParams(@NonNull ByteArrayParceledSlice value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mSerializedAppParams = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull ExecuteInputParcel build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mAppPackageName = "";
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mSerializedAppParams = null;
+            }
+            ExecuteInputParcel o = new ExecuteInputParcel(
+                    mAppPackageName,
+                    mSerializedAppParams);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x4) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1708120245903L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/ExecuteInputParcel.java",
+            inputSignatures = " @android.annotation.NonNull java.lang.String mAppPackageName\n @android.annotation.Nullable com.android.ondevicepersonalization.internal.util.ByteArrayParceledSlice mSerializedAppParams\nclass ExecuteInputParcel extends java.lang.Object implements [android.os.Parcelable]\[email protected](genAidl=false, genHiddenBuilder=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/ExecuteOutput.java b/android-35/android/adservices/ondevicepersonalization/ExecuteOutput.java
new file mode 100644
index 0000000..808de96
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/ExecuteOutput.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The result returned by
+ * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)} in response to a call to
+ * {@code OnDevicePersonalizationManager#execute(ComponentName, PersistableBundle,
+ * java.util.concurrent.Executor, OutcomeReceiver)}
+ * from a client app.
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public final class ExecuteOutput {
+    /**
+     * Persistent data to be written to the REQUESTS table after
+     * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}
+     * completes. If null, no persistent data will be written.
+     */
+    @DataClass.MaySetToNull
+    @Nullable private RequestLogRecord mRequestLogRecord = null;
+
+    /**
+     * A {@link RenderingConfig} object that contains information about the content to be rendered
+     * in the client app view. Can be null if no content is to be rendered.
+     */
+    @DataClass.MaySetToNull
+    @Nullable private RenderingConfig mRenderingConfig = null;
+
+    /**
+     * A list of {@link EventLogRecord} objects to be written to the EVENTS table. Each
+     * {@link EventLogRecord} must be associated with an existing {@link RequestLogRecord} in
+     * the REQUESTS table, specified using
+     * {@link EventLogRecord.Builder#setRequestLogRecord(RequestLogRecord)}.
+     * If the {@link RequestLogRecord} is not specified, the {@link EventLogRecord} will not be
+     * written.
+     */
+    @DataClass.PluralOf("eventLogRecord")
+    @NonNull private List<EventLogRecord> mEventLogRecords = Collections.emptyList();
+
+    /**
+     * A byte array that an {@link IsolatedService} may optionally return to to a calling app,
+     * by setting this field to a non-null value.
+     * The contents of this array will be returned to the caller of
+     * {@link OnDevicePersonalizationManager#execute(ComponentName, PersistableBundle, java.util.concurrent.Executor, OutcomeReceiver)}
+     * if returning data from isolated processes is allowed by policy and the
+     * (calling app package, isolated service package) pair is present in an allowlist that
+     * permits data to be returned.
+     */
+    @DataClass.MaySetToNull
+    @Nullable private byte[] mOutputData = null;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ ExecuteOutput(
+            @Nullable RequestLogRecord requestLogRecord,
+            @Nullable RenderingConfig renderingConfig,
+            @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
+    }
+
+    /**
+     * Persistent data to be written to the REQUESTS table after
+     * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}
+     * completes. If null, no persistent data will be written.
+     */
+    @DataClass.Generated.Member
+    public @Nullable RequestLogRecord getRequestLogRecord() {
+        return mRequestLogRecord;
+    }
+
+    /**
+     * A {@link RenderingConfig} object that contains information about the content to be rendered
+     * in the client app view. Can be null if no content is to be rendered.
+     */
+    @DataClass.Generated.Member
+    public @Nullable RenderingConfig getRenderingConfig() {
+        return mRenderingConfig;
+    }
+
+    /**
+     * A list of {@link EventLogRecord} objects to be written to the EVENTS table. Each
+     * {@link EventLogRecord} must be associated with an existing {@link RequestLogRecord} in
+     * the REQUESTS table, specified using
+     * {@link EventLogRecord.Builder#setRequestLogRecord(RequestLogRecord)}.
+     * If the {@link RequestLogRecord} is not specified, the {@link EventLogRecord} will not be
+     * written.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<EventLogRecord> getEventLogRecords() {
+        return mEventLogRecords;
+    }
+
+    /**
+     * A byte array that an {@link IsolatedService} may optionally return to to a calling app,
+     * by setting this field to a non-null value.
+     * The contents of this array will be returned to the caller of
+     * {@link OnDevicePersonalizationManager#execute(ComponentName, PersistableBundle, java.util.concurrent.Executor, OutcomeReceiver)}
+     * if returning data from isolated processes is allowed by policy and the
+     * (calling app package, isolated service package) pair is present in an allowlist that
+     * permits data to be returned.
+     */
+    @DataClass.Generated.Member
+    public @Nullable byte[] getOutputData() {
+        return mOutputData;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(ExecuteOutput other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        ExecuteOutput that = (ExecuteOutput) o;
+        //noinspection PointlessBooleanExpression
+        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.Arrays.equals(mOutputData, that.mOutputData);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _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;
+    }
+
+    /**
+     * A builder for {@link ExecuteOutput}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @Nullable RequestLogRecord mRequestLogRecord;
+        private @Nullable RenderingConfig mRenderingConfig;
+        private @NonNull List<EventLogRecord> mEventLogRecords;
+        private @Nullable byte[] mOutputData;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * Persistent data to be written to the REQUESTS table after
+         * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}
+         * completes. If null, no persistent data will be written.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setRequestLogRecord(@Nullable RequestLogRecord value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mRequestLogRecord = value;
+            return this;
+        }
+
+        /**
+         * A {@link RenderingConfig} object that contains information about the content to be rendered
+         * in the client app view. Can be null if no content is to be rendered.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setRenderingConfig(@Nullable RenderingConfig value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mRenderingConfig = value;
+            return this;
+        }
+
+        /**
+         * A list of {@link EventLogRecord} objects to be written to the EVENTS table. Each
+         * {@link EventLogRecord} must be associated with an existing {@link RequestLogRecord} in
+         * the REQUESTS table, specified using
+         * {@link EventLogRecord.Builder#setRequestLogRecord(RequestLogRecord)}.
+         * If the {@link RequestLogRecord} is not specified, the {@link EventLogRecord} will not be
+         * written.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setEventLogRecords(@NonNull List<EventLogRecord> value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mEventLogRecords = value;
+            return this;
+        }
+
+        /** @see #setEventLogRecords */
+        @DataClass.Generated.Member
+        public @NonNull Builder addEventLogRecord(@NonNull EventLogRecord value) {
+            if (mEventLogRecords == null) setEventLogRecords(new java.util.ArrayList<>());
+            mEventLogRecords.add(value);
+            return this;
+        }
+
+        /**
+         * A byte array that an {@link IsolatedService} may optionally return to to a calling app,
+         * by setting this field to a non-null value.
+         * The contents of this array will be returned to the caller of
+         * {@link OnDevicePersonalizationManager#execute(ComponentName, PersistableBundle, java.util.concurrent.Executor, OutcomeReceiver)}
+         * if returning data from isolated processes is allowed by policy and the
+         * (calling app package, isolated service package) pair is present in an allowlist that
+         * permits data to be returned.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setOutputData(@Nullable 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 |= 0x10; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mRequestLogRecord = null;
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mRenderingConfig = null;
+            }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mEventLogRecords = Collections.emptyList();
+            }
+            if ((mBuilderFieldsSet & 0x8) == 0) {
+                mOutputData = null;
+            }
+            ExecuteOutput o = new ExecuteOutput(
+                    mRequestLogRecord,
+                    mRenderingConfig,
+                    mEventLogRecords,
+                    mOutputData);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x10) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1707251143585L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.java",
+            inputSignatures = "private @com.android.ondevicepersonalization.internal.util.DataClass.MaySetToNull @android.annotation.Nullable android.adservices.ondevicepersonalization.RequestLogRecord mRequestLogRecord\nprivate @com.android.ondevicepersonalization.internal.util.DataClass.MaySetToNull @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 @com.android.ondevicepersonalization.internal.util.DataClass.MaySetToNull @android.annotation.Nullable byte[] mOutputData\nclass ExecuteOutput extends java.lang.Object implements []\[email protected](genBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/ExecuteOutputParcel.java b/android-35/android/adservices/ondevicepersonalization/ExecuteOutputParcel.java
new file mode 100644
index 0000000..cf797ba
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/ExecuteOutputParcel.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Parcelable version of {@link ExecuteOutput}.
+ * @hide
+ */
+@DataClass(genAidl = false, genBuilder = false)
+public final class ExecuteOutputParcel implements Parcelable {
+    /**
+     * Persistent data to be written to the REQUESTS table after
+     * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}
+     * completes. If null, no persistent data will be written.
+     */
+    @Nullable private RequestLogRecord mRequestLogRecord = null;
+
+    /**
+     * A {@link RenderingConfig} object that contains information about the content to be rendered
+     * in the client app view. Can be null if no content is to be rendered.
+     */
+    @Nullable private RenderingConfig mRenderingConfig = null;
+
+    /**
+     * A list of {@link EventLogRecord}. Writes events to the EVENTS table and associates
+     * them with requests with the specified corresponding {@link RequestLogRecord} from
+     * {@link EventLogRecord#getRequestLogRecord()}.
+     * If the event does not contain a {@link RequestLogRecord} emitted by this package, the
+     * EventLogRecord is not written.
+     *
+     */
+    @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(),
+                value.getOutputData());
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/ExecuteOutputParcel.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new ExecuteOutputParcel.
+     *
+     * @param requestLogRecord
+     *   Persistent data to be written to the REQUESTS table after
+     *   {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}
+     *   completes. If null, no persistent data will be written.
+     * @param renderingConfig
+     *   A {@link RenderingConfig} object that contains information about the content to be rendered
+     *   in the client app view. Can be null if no content is to be rendered.
+     * @param eventLogRecords
+     *   A list of {@link EventLogRecord}. Writes events to the EVENTS table and associates
+     *   them with requests with the specified corresponding {@link RequestLogRecord} from
+     *   {@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,
+            @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
+    }
+
+    /**
+     * Persistent data to be written to the REQUESTS table after
+     * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}
+     * completes. If null, no persistent data will be written.
+     */
+    @DataClass.Generated.Member
+    public @Nullable RequestLogRecord getRequestLogRecord() {
+        return mRequestLogRecord;
+    }
+
+    /**
+     * A {@link RenderingConfig} object that contains information about the content to be rendered
+     * in the client app view. Can be null if no content is to be rendered.
+     */
+    @DataClass.Generated.Member
+    public @Nullable RenderingConfig getRenderingConfig() {
+        return mRenderingConfig;
+    }
+
+    /**
+     * A list of {@link EventLogRecord}. Writes events to the EVENTS table and associates
+     * them with requests with the specified corresponding {@link RequestLogRecord} from
+     * {@link EventLogRecord#getRequestLogRecord()}.
+     * If the event does not contain a {@link RequestLogRecord} emitted by this package, the
+     * EventLogRecord is not written.
+     */
+    @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) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mRequestLogRecord != null) flg |= 0x1;
+        if (mRenderingConfig != null) flg |= 0x2;
+        dest.writeByte(flg);
+        if (mRequestLogRecord != null) dest.writeTypedObject(mRequestLogRecord, flags);
+        if (mRenderingConfig != null) dest.writeTypedObject(mRenderingConfig, flags);
+        dest.writeParcelableList(mEventLogRecords, flags);
+        dest.writeByteArray(mOutputData);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ ExecuteOutputParcel(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        RequestLogRecord requestLogRecord = (flg & 0x1) == 0 ? null : (RequestLogRecord) in.readTypedObject(RequestLogRecord.CREATOR);
+        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
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ExecuteOutputParcel> CREATOR
+            = new Parcelable.Creator<ExecuteOutputParcel>() {
+        @Override
+        public ExecuteOutputParcel[] newArray(int size) {
+            return new ExecuteOutputParcel[size];
+        }
+
+        @Override
+        public ExecuteOutputParcel createFromParcel(@NonNull android.os.Parcel in) {
+            return new ExecuteOutputParcel(in);
+        }
+    };
+
+    @DataClass.Generated(
+            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\nprivate @android.annotation.Nullable byte[] mOutputData\nclass ExecuteOutputParcel extends java.lang.Object implements [android.os.Parcelable]\[email protected](genAidl=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/FederatedComputeInput.java b/android-35/android/adservices/ondevicepersonalization/FederatedComputeInput.java
new file mode 100644
index 0000000..b2051e6
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/FederatedComputeInput.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/** The input data for {@link FederatedComputeScheduler#schedule}. */
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public final class FederatedComputeInput {
+    // TODO(b/300461799): add federated compute server document.
+    /**
+     * Population refers to a collection of devices that specific task groups can run on. It should
+     * match task plan configured at remote federated compute server.
+     */
+    @NonNull private String mPopulationName = "";
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen
+    // $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/FederatedComputeInput.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    // @formatter:off
+
+    @DataClass.Generated.Member
+        /* package-private */ FederatedComputeInput(@NonNull String populationName) {
+        this.mPopulationName = populationName;
+        AnnotationValidations.validate(NonNull.class, null, mPopulationName);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Population refers to a collection of devices that specific task groups can run on. It should
+     * match task plan configured at remote federated compute server.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getPopulationName() {
+        return mPopulationName;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(FederatedComputeInput other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        FederatedComputeInput that = (FederatedComputeInput) o;
+        //noinspection PointlessBooleanExpression
+        return true && java.util.Objects.equals(mPopulationName, that.mPopulationName);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mPopulationName);
+        return _hash;
+    }
+
+    /** A builder for {@link FederatedComputeInput} */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @NonNull String mPopulationName;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {}
+
+        /** Setter for {@link #getPopulationName}. */
+        @DataClass.Generated.Member
+        public @NonNull Builder setPopulationName(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mPopulationName = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull FederatedComputeInput build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mPopulationName = "";
+            }
+            FederatedComputeInput o = new FederatedComputeInput(mPopulationName);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x2) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1697578140247L,
+            codegenVersion = "1.0.23",
+            sourceFile =
+                    "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/FederatedComputeInput.java",
+            inputSignatures =
+                    "private @android.annotation.NonNull java.lang.String mPopulationName\nclass FederatedComputeInput extends java.lang.Object implements []\[email protected](genBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+    // @formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/FederatedComputeScheduler.java b/android-35/android/adservices/ondevicepersonalization/FederatedComputeScheduler.java
new file mode 100644
index 0000000..2cb5c28
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/FederatedComputeScheduler.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.adservices.ondevicepersonalization.aidl.IDataAccessService;
+import android.adservices.ondevicepersonalization.aidl.IFederatedComputeCallback;
+import android.adservices.ondevicepersonalization.aidl.IFederatedComputeService;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+import android.federatedcompute.common.TrainingOptions;
+import android.os.RemoteException;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.LoggerFactory;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Handles scheduling federated compute jobs. See {@link
+ * IsolatedService#getFederatedComputeScheduler}.
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public class FederatedComputeScheduler {
+    private static final String TAG = FederatedComputeScheduler.class.getSimpleName();
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
+
+    private final IFederatedComputeService mFcService;
+    private final IDataAccessService mDataAccessService;
+
+    /** @hide */
+    public FederatedComputeScheduler(
+            IFederatedComputeService binder, IDataAccessService dataService) {
+        mFcService = binder;
+        mDataAccessService = dataService;
+    }
+
+    // TODO(b/300461799): add federated compute server document.
+    // TODO(b/269665435): add sample code snippet.
+    /**
+     * Schedules a federated compute job. In {@link IsolatedService#onRequest}, the app can call
+     * {@link IsolatedService#getFederatedComputeScheduler} to pass scheduler when construct {@link
+     * IsolatedWorker}.
+     *
+     * @param params parameters related to job scheduling.
+     * @param input the configuration of the federated compute. It should be consistent with the
+     *     federated compute server setup.
+     */
+    @WorkerThread
+    public void schedule(@NonNull Params params, @NonNull FederatedComputeInput input) {
+        final long startTimeMillis = System.currentTimeMillis();
+        int responseCode = Constants.STATUS_INTERNAL_ERROR;
+        if (mFcService == null) {
+            throw new IllegalStateException(
+                    "FederatedComputeScheduler not available for this instance.");
+        }
+
+        android.federatedcompute.common.TrainingInterval trainingInterval =
+                convertTrainingInterval(params.getTrainingInterval());
+        TrainingOptions trainingOptions =
+                new TrainingOptions.Builder()
+                        .setPopulationName(input.getPopulationName())
+                        .setTrainingInterval(trainingInterval)
+                        .build();
+        CountDownLatch latch = new CountDownLatch(1);
+        final int[] err = {0};
+        try {
+            mFcService.schedule(
+                    trainingOptions,
+                    new IFederatedComputeCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            latch.countDown();
+                        }
+
+                        @Override
+                        public void onFailure(int i) {
+                            err[0] = i;
+                            latch.countDown();
+                        }
+                    });
+            latch.await();
+            if (err[0] != 0) {
+                throw new IllegalStateException("Internal failure occurred while scheduling job");
+            }
+            responseCode = Constants.STATUS_SUCCESS;
+        } catch (RemoteException | InterruptedException e) {
+            sLogger.e(TAG + ": Failed to schedule federated compute job", e);
+            throw new IllegalStateException(e);
+        } finally {
+            logApiCallStats(
+                    Constants.API_NAME_FEDERATED_COMPUTE_SCHEDULE,
+                    System.currentTimeMillis() - startTimeMillis,
+                    responseCode);
+        }
+    }
+
+    /**
+     * Cancels a federated compute job with input training params. In {@link
+     * IsolatedService#onRequest}, the app can call {@link
+     * IsolatedService#getFederatedComputeScheduler} to pass scheduler when construct {@link
+     * IsolatedWorker}.
+     *
+     * @param input the configuration of the federated compute. It should be consistent with the
+     *     federated compute server setup.
+     */
+    @WorkerThread
+    public void cancel(@NonNull FederatedComputeInput input) {
+        final long startTimeMillis = System.currentTimeMillis();
+        int responseCode = Constants.STATUS_INTERNAL_ERROR;
+        if (mFcService == null) {
+            throw new IllegalStateException(
+                    "FederatedComputeScheduler not available for this instance.");
+        }
+        CountDownLatch latch = new CountDownLatch(1);
+        final int[] err = {0};
+        try {
+            mFcService.cancel(
+                    input.getPopulationName(),
+                    new IFederatedComputeCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            latch.countDown();
+                        }
+
+                        @Override
+                        public void onFailure(int i) {
+                            err[0] = i;
+                            latch.countDown();
+                        }
+                    });
+            latch.await();
+            if (err[0] != 0) {
+                throw new IllegalStateException("Internal failure occurred while cancelling job");
+            }
+            responseCode = Constants.STATUS_SUCCESS;
+        } catch (RemoteException | InterruptedException e) {
+            sLogger.e(TAG + ": Failed to cancel federated compute job", e);
+            throw new IllegalStateException(e);
+        } finally {
+            logApiCallStats(
+                    Constants.API_NAME_FEDERATED_COMPUTE_CANCEL,
+                    System.currentTimeMillis() - startTimeMillis,
+                    responseCode);
+        }
+    }
+
+    private android.federatedcompute.common.TrainingInterval convertTrainingInterval(
+            TrainingInterval interval) {
+        return new android.federatedcompute.common.TrainingInterval.Builder()
+                .setMinimumIntervalMillis(interval.getMinimumInterval().toMillis())
+                .setSchedulingMode(convertSchedulingMode(interval))
+                .build();
+    }
+
+    private @android.federatedcompute.common.TrainingInterval.SchedulingMode int
+            convertSchedulingMode(TrainingInterval interval) {
+        switch (interval.getSchedulingMode()) {
+            case TrainingInterval.SCHEDULING_MODE_ONE_TIME:
+                return android.federatedcompute.common.TrainingInterval.SCHEDULING_MODE_ONE_TIME;
+            case TrainingInterval.SCHEDULING_MODE_RECURRENT:
+                return android.federatedcompute.common.TrainingInterval.SCHEDULING_MODE_RECURRENT;
+            default:
+                throw new IllegalStateException(
+                        "Unsupported scheduling mode " + interval.getSchedulingMode());
+        }
+    }
+
+    private void logApiCallStats(int apiName, long duration, int responseCode) {
+        try {
+            mDataAccessService.logApiCallStats(apiName, duration, responseCode);
+        } catch (Exception e) {
+            sLogger.d(e, TAG + ": failed to log metrics");
+        }
+    }
+
+    /** The parameters related to job scheduling. */
+    public static class Params {
+        /**
+         * If training interval is scheduled for recurrent tasks, the earliest time this task could
+         * start is after the minimum training interval expires. E.g. If the task is set to run
+         * maximum once per day, the first run of this task will be one day after this task is
+         * scheduled. When a one time job is scheduled, the earliest next runtime is calculated
+         * based on federated compute default interval.
+         */
+        @NonNull private final TrainingInterval mTrainingInterval;
+
+        public Params(@NonNull TrainingInterval trainingInterval) {
+            mTrainingInterval = trainingInterval;
+        }
+
+        @NonNull
+        public TrainingInterval getTrainingInterval() {
+            return mTrainingInterval;
+        }
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/InferenceInput.java b/android-35/android/adservices/ondevicepersonalization/InferenceInput.java
new file mode 100644
index 0000000..24f3f4f
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/InferenceInput.java
@@ -0,0 +1,653 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Contains all the information needed for a run of model inference. The input of {@link
+ * ModelManager#run}.
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public final class InferenceInput {
+    /** The configuration that controls runtime interpreter behavior. */
+    @NonNull private Params mParams;
+
+    /**
+     * An array of input data. The inputs should be in the same order as inputs of the model.
+     *
+     * <p>For example, if a model takes multiple inputs:
+     *
+     * <pre>{@code
+     * String[] input0 = {"foo", "bar"}; // string tensor shape is [2].
+     * int[] input1 = new int[]{3, 2, 1}; // int tensor shape is [3].
+     * Object[] inputData = {input0, input1, ...};
+     * }</pre>
+     *
+     * For TFLite, this field is mapped to inputs of runForMultipleInputsOutputs:
+     * https://www.tensorflow.org/lite/api_docs/java/org/tensorflow/lite/InterpreterApi#parameters_9
+     */
+    @NonNull private Object[] mInputData;
+
+    /**
+     * The number of input examples. Adopter can set this field to run batching inference. The batch
+     * size is 1 by default. The batch size should match the input data size.
+     */
+    private int mBatchSize = 1;
+
+    /**
+     * The empty InferenceOutput representing the expected output structure. For TFLite, the
+     * inference code will verify whether this expected output structure matches model output
+     * signature.
+     *
+     * <p>If a model produce string tensors:
+     *
+     * <pre>{@code
+     * String[] output = new String[3][2];  // Output tensor shape is [3, 2].
+     * HashMap<Integer, Object> outputs = new HashMap<>();
+     * outputs.put(0, output);
+     * expectedOutputStructure = new InferenceOutput.Builder().setDataOutputs(outputs).build();
+     * }</pre>
+     */
+    @NonNull private InferenceOutput mExpectedOutputStructure;
+
+    @DataClass(genBuilder = true, genHiddenConstructor = true, genEqualsHashCode = true)
+    public static class Params {
+        /**
+         * A {@link KeyValueStore} where pre-trained model is stored. Only supports TFLite model
+         * now.
+         */
+        @NonNull private KeyValueStore mKeyValueStore;
+
+        /**
+         * The key of the table where the corresponding value stores a pre-trained model. Only
+         * supports TFLite model now.
+         */
+        @NonNull private String mModelKey;
+
+        /** The model inference will run on CPU. */
+        public static final int DELEGATE_CPU = 1;
+
+        /**
+         * The delegate to run model inference.
+         *
+         * @hide
+         */
+        @IntDef(
+                prefix = "DELEGATE_",
+                value = {DELEGATE_CPU})
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface Delegate {}
+
+        /**
+         * The delegate to run model inference. If not set, the default value is {@link
+         * #DELEGATE_CPU}.
+         */
+        private @Delegate int mDelegateType = DELEGATE_CPU;
+
+        /** The model is a tensorflow lite model. */
+        public static final int MODEL_TYPE_TENSORFLOW_LITE = 1;
+
+        /**
+         * The type of the model.
+         *
+         * @hide
+         */
+        @IntDef(
+                prefix = "MODEL_TYPE",
+                value = {MODEL_TYPE_TENSORFLOW_LITE})
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface ModelType {}
+
+        /**
+         * The type of the pre-trained model. If not set, the default value is {@link
+         * #MODEL_TYPE_TENSORFLOW_LITE} . Only supports {@link #MODEL_TYPE_TENSORFLOW_LITE} for now.
+         */
+        private @ModelType int mModelType = MODEL_TYPE_TENSORFLOW_LITE;
+
+        /**
+         * The number of threads used for intraop parallelism on CPU, must be positive number.
+         * Adopters can set this field based on model architecture. The actual thread number depends
+         * on system resources and other constraints.
+         */
+        private @IntRange(from = 1) int mRecommendedNumThreads = 1;
+
+        // Code below generated by codegen v1.0.23.
+        //
+        // DO NOT MODIFY!
+        // CHECKSTYLE:OFF Generated code
+        //
+        // To regenerate run:
+        // $ codegen
+        // $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/InferenceInput.java
+        //
+        // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+        //   Settings > Editor > Code Style > Formatter Control
+        // @formatter:off
+
+        /**
+         * Creates a new Params.
+         *
+         * @param keyValueStore A {@link KeyValueStore} where pre-trained model is stored. Only
+         *     supports TFLite model now.
+         * @param modelKey The key of the table where the corresponding value stores a pre-trained
+         *     model. Only supports TFLite model now.
+         * @param delegateType The delegate to run model inference. If not set, the default value is
+         *     {@link #DELEGATE_CPU}.
+         * @param modelType The type of the pre-trained model. If not set, the default value is
+         *     {@link #MODEL_TYPE_TENSORFLOW_LITE} . Only supports {@link
+         *     #MODEL_TYPE_TENSORFLOW_LITE} for now.
+         * @param recommendedNumThreads The number of threads used for intraop parallelism on CPU,
+         *     must be positive number. Adopters can set this field based on model architecture. The
+         *     actual thread number depends on system resources and other constraints.
+         * @hide
+         */
+        @DataClass.Generated.Member
+        public Params(
+                @NonNull KeyValueStore keyValueStore,
+                @NonNull String modelKey,
+                @Delegate int delegateType,
+                @ModelType int modelType,
+                @IntRange(from = 1) int recommendedNumThreads) {
+            this.mKeyValueStore = keyValueStore;
+            AnnotationValidations.validate(NonNull.class, null, mKeyValueStore);
+            this.mModelKey = modelKey;
+            AnnotationValidations.validate(NonNull.class, null, mModelKey);
+            this.mDelegateType = delegateType;
+            AnnotationValidations.validate(Delegate.class, null, mDelegateType);
+            this.mModelType = modelType;
+            AnnotationValidations.validate(ModelType.class, null, mModelType);
+            this.mRecommendedNumThreads = recommendedNumThreads;
+            AnnotationValidations.validate(IntRange.class, null, mRecommendedNumThreads, "from", 1);
+
+            // onConstructed(); // You can define this method to get a callback
+        }
+
+        /**
+         * A {@link KeyValueStore} where pre-trained model is stored. Only supports TFLite model
+         * now.
+         */
+        @DataClass.Generated.Member
+        public @NonNull KeyValueStore getKeyValueStore() {
+            return mKeyValueStore;
+        }
+
+        /**
+         * The key of the table where the corresponding value stores a pre-trained model. Only
+         * supports TFLite model now.
+         */
+        @DataClass.Generated.Member
+        public @NonNull String getModelKey() {
+            return mModelKey;
+        }
+
+        /**
+         * The delegate to run model inference. If not set, the default value is {@link
+         * #DELEGATE_CPU}.
+         */
+        @DataClass.Generated.Member
+        public @Delegate int getDelegateType() {
+            return mDelegateType;
+        }
+
+        /**
+         * The type of the pre-trained model. If not set, the default value is {@link
+         * #MODEL_TYPE_TENSORFLOW_LITE} . Only supports {@link #MODEL_TYPE_TENSORFLOW_LITE} for now.
+         */
+        @DataClass.Generated.Member
+        public @ModelType int getModelType() {
+            return mModelType;
+        }
+
+        /**
+         * The number of threads used for intraop parallelism on CPU, must be positive number.
+         * Adopters can set this field based on model architecture. The actual thread number depends
+         * on system resources and other constraints.
+         */
+        @DataClass.Generated.Member
+        public @IntRange(from = 1) int getRecommendedNumThreads() {
+            return mRecommendedNumThreads;
+        }
+
+        @Override
+        @DataClass.Generated.Member
+        public boolean equals(@android.annotation.Nullable Object o) {
+            // You can override field equality logic by defining either of the methods like:
+            // boolean fieldNameEquals(Params other) { ... }
+            // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            @SuppressWarnings("unchecked")
+            Params that = (Params) o;
+            //noinspection PointlessBooleanExpression
+            return true
+                    && java.util.Objects.equals(mKeyValueStore, that.mKeyValueStore)
+                    && java.util.Objects.equals(mModelKey, that.mModelKey)
+                    && mDelegateType == that.mDelegateType
+                    && mModelType == that.mModelType
+                    && mRecommendedNumThreads == that.mRecommendedNumThreads;
+        }
+
+        @Override
+        @DataClass.Generated.Member
+        public int hashCode() {
+            // You can override field hashCode logic by defining methods like:
+            // int fieldNameHashCode() { ... }
+
+            int _hash = 1;
+            _hash = 31 * _hash + java.util.Objects.hashCode(mKeyValueStore);
+            _hash = 31 * _hash + java.util.Objects.hashCode(mModelKey);
+            _hash = 31 * _hash + mDelegateType;
+            _hash = 31 * _hash + mModelType;
+            _hash = 31 * _hash + mRecommendedNumThreads;
+            return _hash;
+        }
+
+        /** A builder for {@link Params} */
+        @SuppressWarnings("WeakerAccess")
+        @DataClass.Generated.Member
+        public static final class Builder {
+
+            private @NonNull KeyValueStore mKeyValueStore;
+            private @NonNull String mModelKey;
+            private @Delegate int mDelegateType;
+            private @ModelType int mModelType;
+            private @IntRange(from = 1) int mRecommendedNumThreads;
+
+            private long mBuilderFieldsSet = 0L;
+
+            /**
+             * Creates a new Builder.
+             *
+             * @param keyValueStore A {@link KeyValueStore} where pre-trained model is stored. Only
+             *     supports TFLite model now.
+             * @param modelKey The key of the table where the corresponding value stores a
+             *     pre-trained model. Only supports TFLite model now.
+             */
+            public Builder(@NonNull KeyValueStore keyValueStore, @NonNull String modelKey) {
+                mKeyValueStore = keyValueStore;
+                AnnotationValidations.validate(NonNull.class, null, mKeyValueStore);
+                mModelKey = modelKey;
+                AnnotationValidations.validate(NonNull.class, null, mModelKey);
+            }
+
+            /**
+             * A {@link KeyValueStore} where pre-trained model is stored. Only supports TFLite model
+             * now.
+             */
+            @DataClass.Generated.Member
+            public @NonNull Builder setKeyValueStore(@NonNull KeyValueStore value) {
+                mBuilderFieldsSet |= 0x1;
+                mKeyValueStore = value;
+                return this;
+            }
+
+            /**
+             * The key of the table where the corresponding value stores a pre-trained model. Only
+             * supports TFLite model now.
+             */
+            @DataClass.Generated.Member
+            public @NonNull Builder setModelKey(@NonNull String value) {
+                mBuilderFieldsSet |= 0x2;
+                mModelKey = value;
+                return this;
+            }
+
+            /**
+             * The delegate to run model inference. If not set, the default value is {@link
+             * #DELEGATE_CPU}.
+             */
+            @DataClass.Generated.Member
+            public @NonNull Builder setDelegateType(@Delegate int value) {
+                mBuilderFieldsSet |= 0x4;
+                mDelegateType = value;
+                return this;
+            }
+
+            /**
+             * The type of the pre-trained model. If not set, the default value is {@link
+             * #MODEL_TYPE_TENSORFLOW_LITE} . Only supports {@link #MODEL_TYPE_TENSORFLOW_LITE} for
+             * now.
+             */
+            @DataClass.Generated.Member
+            public @NonNull Builder setModelType(@ModelType int value) {
+                mBuilderFieldsSet |= 0x8;
+                mModelType = value;
+                return this;
+            }
+
+            /**
+             * The number of threads used for intraop parallelism on CPU, must be positive number.
+             * Adopters can set this field based on model architecture. The actual thread number
+             * depends on system resources and other constraints.
+             */
+            @DataClass.Generated.Member
+            public @NonNull Builder setRecommendedNumThreads(@IntRange(from = 1) int value) {
+                mBuilderFieldsSet |= 0x10;
+                mRecommendedNumThreads = value;
+                return this;
+            }
+
+            /** Builds the instance. */
+            public @NonNull Params build() {
+                mBuilderFieldsSet |= 0x20; // Mark builder used
+
+                if ((mBuilderFieldsSet & 0x4) == 0) {
+                    mDelegateType = DELEGATE_CPU;
+                }
+                if ((mBuilderFieldsSet & 0x8) == 0) {
+                    mModelType = MODEL_TYPE_TENSORFLOW_LITE;
+                }
+                if ((mBuilderFieldsSet & 0x10) == 0) {
+                    mRecommendedNumThreads = 1;
+                }
+                Params o =
+                        new Params(
+                                mKeyValueStore,
+                                mModelKey,
+                                mDelegateType,
+                                mModelType,
+                                mRecommendedNumThreads);
+                return o;
+            }
+        }
+
+        @DataClass.Generated(
+                time = 1709250081597L,
+                codegenVersion = "1.0.23",
+                sourceFile =
+                        "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/InferenceInput.java",
+                inputSignatures =
+                        "private @android.annotation.NonNull android.adservices.ondevicepersonalization.KeyValueStore mKeyValueStore\nprivate @android.annotation.NonNull java.lang.String mModelKey\npublic static final  int DELEGATE_CPU\nprivate @android.adservices.ondevicepersonalization.Params.Delegate int mDelegateType\npublic static final  int MODEL_TYPE_TENSORFLOW_LITE\nprivate @android.adservices.ondevicepersonalization.Params.ModelType int mModelType\nprivate @android.annotation.IntRange int mRecommendedNumThreads\nclass Params extends java.lang.Object implements []\[email protected](genBuilder=true, genHiddenConstructor=true, genEqualsHashCode=true)")
+        @Deprecated
+        private void __metadata() {}
+
+        // @formatter:on
+        // End of generated code
+
+    }
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen
+    // $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/InferenceInput.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    // @formatter:off
+
+    @DataClass.Generated.Member
+    /* package-private */ InferenceInput(
+            @NonNull Params params,
+            @NonNull Object[] inputData,
+            int batchSize,
+            @NonNull InferenceOutput expectedOutputStructure) {
+        this.mParams = params;
+        AnnotationValidations.validate(NonNull.class, null, mParams);
+        this.mInputData = inputData;
+        AnnotationValidations.validate(NonNull.class, null, mInputData);
+        this.mBatchSize = batchSize;
+        this.mExpectedOutputStructure = expectedOutputStructure;
+        AnnotationValidations.validate(NonNull.class, null, mExpectedOutputStructure);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /** The configuration that controls runtime interpreter behavior. */
+    @DataClass.Generated.Member
+    public @NonNull Params getParams() {
+        return mParams;
+    }
+
+    /**
+     * An array of input data. The inputs should be in the same order as inputs of the model.
+     *
+     * <p>For example, if a model takes multiple inputs:
+     *
+     * <pre>{@code
+     * String[] input0 = {"foo", "bar"}; // string tensor shape is [2].
+     * int[] input1 = new int[]{3, 2, 1}; // int tensor shape is [3].
+     * Object[] inputData = {input0, input1, ...};
+     * }</pre>
+     *
+     * For TFLite, this field is mapped to inputs of runForMultipleInputsOutputs:
+     * https://www.tensorflow.org/lite/api_docs/java/org/tensorflow/lite/InterpreterApi#parameters_9
+     */
+    @SuppressLint("ArrayReturn")
+    @DataClass.Generated.Member
+    public @NonNull Object[] getInputData() {
+        return mInputData;
+    }
+
+    /**
+     * The number of input examples. Adopter can set this field to run batching inference. The batch
+     * size is 1 by default. The batch size should match the input data size.
+     */
+    @DataClass.Generated.Member
+    public int getBatchSize() {
+        return mBatchSize;
+    }
+
+    /**
+     * The empty InferenceOutput representing the expected output structure. For TFLite, the
+     * inference code will verify whether this expected output structure matches model output
+     * signature.
+     *
+     * <p>If a model produce string tensors:
+     *
+     * <pre>{@code
+     * String[] output = new String[3][2];  // Output tensor shape is [3, 2].
+     * HashMap<Integer, Object> outputs = new HashMap<>();
+     * outputs.put(0, output);
+     * expectedOutputStructure = new InferenceOutput.Builder().setDataOutputs(outputs).build();
+     * }</pre>
+     */
+    @DataClass.Generated.Member
+    public @NonNull InferenceOutput getExpectedOutputStructure() {
+        return mExpectedOutputStructure;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(InferenceInput other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        InferenceInput that = (InferenceInput) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mParams, that.mParams)
+                && java.util.Arrays.equals(mInputData, that.mInputData)
+                && mBatchSize == that.mBatchSize
+                && java.util.Objects.equals(
+                        mExpectedOutputStructure, that.mExpectedOutputStructure);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mParams);
+        _hash = 31 * _hash + java.util.Arrays.hashCode(mInputData);
+        _hash = 31 * _hash + mBatchSize;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mExpectedOutputStructure);
+        return _hash;
+    }
+
+    /** A builder for {@link InferenceInput} */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @NonNull Params mParams;
+        private @NonNull Object[] mInputData;
+        private int mBatchSize;
+        private @NonNull InferenceOutput mExpectedOutputStructure;
+
+        private long mBuilderFieldsSet = 0L;
+
+        /**
+         * Creates a new Builder.
+         *
+         * @param params The configuration that controls runtime interpreter behavior.
+         * @param inputData An array of input data. The inputs should be in the same order as inputs
+         *     of the model.
+         *     <p>For example, if a model takes multiple inputs:
+         *     <pre>{@code
+         * String[] input0 = {"foo", "bar"}; // string tensor shape is [2].
+         * int[] input1 = new int[]{3, 2, 1}; // int tensor shape is [3].
+         * Object[] inputData = {input0, input1, ...};
+         *
+         * }</pre>
+         *     For TFLite, this field is mapped to inputs of runForMultipleInputsOutputs:
+         *     https://www.tensorflow.org/lite/api_docs/java/org/tensorflow/lite/InterpreterApi#parameters_9
+         * @param expectedOutputStructure The empty InferenceOutput representing the expected output
+         *     structure. For TFLite, the inference code will verify whether this expected output
+         *     structure matches model output signature.
+         *     <p>If a model produce string tensors:
+         *     <pre>{@code
+         * String[] output = new String[3][2];  // Output tensor shape is [3, 2].
+         * HashMap<Integer, Object> outputs = new HashMap<>();
+         * outputs.put(0, output);
+         * expectedOutputStructure = new InferenceOutput.Builder().setDataOutputs(outputs).build();
+         *
+         * }</pre>
+         */
+        public Builder(
+                @NonNull Params params,
+                @SuppressLint("ArrayReturn") @NonNull Object[] inputData,
+                @NonNull InferenceOutput expectedOutputStructure) {
+            mParams = params;
+            AnnotationValidations.validate(NonNull.class, null, mParams);
+            mInputData = inputData;
+            AnnotationValidations.validate(NonNull.class, null, mInputData);
+            mExpectedOutputStructure = expectedOutputStructure;
+            AnnotationValidations.validate(NonNull.class, null, mExpectedOutputStructure);
+        }
+
+        /** The configuration that controls runtime interpreter behavior. */
+        @DataClass.Generated.Member
+        public @NonNull Builder setParams(@NonNull Params value) {
+            mBuilderFieldsSet |= 0x1;
+            mParams = value;
+            return this;
+        }
+
+        /**
+         * An array of input data. The inputs should be in the same order as inputs of the model.
+         *
+         * <p>For example, if a model takes multiple inputs:
+         *
+         * <pre>{@code
+         * String[] input0 = {"foo", "bar"}; // string tensor shape is [2].
+         * int[] input1 = new int[]{3, 2, 1}; // int tensor shape is [3].
+         * Object[] inputData = {input0, input1, ...};
+         * }</pre>
+         *
+         * For TFLite, this field is mapped to inputs of runForMultipleInputsOutputs:
+         * https://www.tensorflow.org/lite/api_docs/java/org/tensorflow/lite/InterpreterApi#parameters_9
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setInputData(@NonNull Object... value) {
+            mBuilderFieldsSet |= 0x2;
+            mInputData = value;
+            return this;
+        }
+
+        /**
+         * The number of input examples. Adopter can set this field to run batching inference. The
+         * batch size is 1 by default. The batch size should match the input data size.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setBatchSize(int value) {
+            mBuilderFieldsSet |= 0x4;
+            mBatchSize = value;
+            return this;
+        }
+
+        /**
+         * The empty InferenceOutput representing the expected output structure. For TFLite, the
+         * inference code will verify whether this expected output structure matches model output
+         * signature.
+         *
+         * <p>If a model produce string tensors:
+         *
+         * <pre>{@code
+         * String[] output = new String[3][2];  // Output tensor shape is [3, 2].
+         * HashMap<Integer, Object> outputs = new HashMap<>();
+         * outputs.put(0, output);
+         * expectedOutputStructure = new InferenceOutput.Builder().setDataOutputs(outputs).build();
+         * }</pre>
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setExpectedOutputStructure(@NonNull InferenceOutput value) {
+            mBuilderFieldsSet |= 0x8;
+            mExpectedOutputStructure = value;
+            return this;
+        }
+
+        /** Builds the instance. */
+        public @NonNull InferenceInput build() {
+            mBuilderFieldsSet |= 0x10; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mBatchSize = 1;
+            }
+            InferenceInput o =
+                    new InferenceInput(mParams, mInputData, mBatchSize, mExpectedOutputStructure);
+            return o;
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1709250081618L,
+            codegenVersion = "1.0.23",
+            sourceFile =
+                    "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/InferenceInput.java",
+            inputSignatures =
+                    "private @android.annotation.NonNull android.adservices.ondevicepersonalization.Params mParams\nprivate @android.annotation.NonNull java.lang.Object[] mInputData\nprivate  int mBatchSize\nprivate @android.annotation.NonNull android.adservices.ondevicepersonalization.InferenceOutput mExpectedOutputStructure\nclass InferenceInput extends java.lang.Object implements []\[email protected](genBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+    // @formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/InferenceInputParcel.java b/android-35/android/adservices/ondevicepersonalization/InferenceInputParcel.java
new file mode 100644
index 0000000..ad7ff56
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/InferenceInputParcel.java
@@ -0,0 +1,280 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.ByteArrayParceledListSlice;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * Parcelable version of {@link InferenceInput}.
+ *
+ * @hide
+ */
+@DataClass(genAidl = false, genBuilder = false)
+public class InferenceInputParcel implements Parcelable {
+    /**
+     * The location of TFLite model. The model is usually store in REMOTE_DATA or LOCAL_DATA table.
+     */
+    @NonNull private ModelId mModelId;
+
+    /** The delegate to run model inference. If not specified, CPU delegate is used by default. */
+    private @InferenceInput.Params.Delegate int mDelegate;
+
+    /**
+     * The number of threads available to the interpreter. Only set and take effective when input
+     * tensors are on CPU. Setting cpuNumThread to 0 has the effect to disable multithreading, which
+     * is equivalent to setting cpuNumThread to 1. If set to the value -1, the number of threads
+     * used will be implementation-defined and platform-dependent.
+     */
+    private @IntRange(from = 1) int mCpuNumThread;
+
+    /** An array of input data. The inputs should be in the same order as inputs of the model. */
+    @NonNull private ByteArrayParceledListSlice mInputData;
+
+    /**
+     * The number of input examples. Adopter can set this field to run batching inference. The batch
+     * size is 1 by default.
+     */
+    private int mBatchSize;
+
+    private @InferenceInput.Params.ModelType int mModelType =
+            InferenceInput.Params.MODEL_TYPE_TENSORFLOW_LITE;
+
+    /**
+     * The empty InferenceOutput representing the expected output structure. For TFLite, the
+     * inference code will verify whether this expected output structure matches model output
+     * signature.
+     */
+    @NonNull private InferenceOutputParcel mExpectedOutputStructure;
+
+    /** @hide */
+    public InferenceInputParcel(@NonNull InferenceInput value) {
+        this(
+                new ModelId.Builder()
+                        .setTableId(value.getParams().getKeyValueStore().getTableId())
+                        .setKey(value.getParams().getModelKey())
+                        .build(),
+                value.getParams().getDelegateType(),
+                value.getParams().getRecommendedNumThreads(),
+                ByteArrayParceledListSlice.create(value.getInputData()),
+                value.getBatchSize(),
+                value.getParams().getModelType(),
+                new InferenceOutputParcel(value.getExpectedOutputStructure()));
+    }
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen
+    // $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/InferenceInputParcel.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    // @formatter:off
+
+    /**
+     * Creates a new InferenceInputParcel.
+     *
+     * @param modelId The location of TFLite model. The model is usually store in REMOTE_DATA or
+     *     LOCAL_DATA table.
+     * @param delegate The delegate to run model inference. If not specified, CPU delegate is used
+     *     by default.
+     * @param cpuNumThread The number of threads available to the interpreter. Only set and take
+     *     effective when input tensors are on CPU. Setting cpuNumThread to 0 has the effect to
+     *     disable multithreading, which is equivalent to setting cpuNumThread to 1. If set to the
+     *     value -1, the number of threads used will be implementation-defined and
+     *     platform-dependent.
+     * @param inputData An array of input data. The inputs should be in the same order as inputs of
+     *     the model.
+     * @param batchSize The number of input examples. Adopter can set this field to run batching
+     *     inference. The batch size is 1 by default.
+     * @param expectedOutputStructure The empty InferenceOutput representing the expected output
+     *     structure. For TFLite, the inference code will verify whether this expected output
+     *     structure matches model output signature.
+     */
+    @DataClass.Generated.Member
+    public InferenceInputParcel(
+            @NonNull ModelId modelId,
+            @InferenceInput.Params.Delegate int delegate,
+            @IntRange(from = 1) int cpuNumThread,
+            @NonNull ByteArrayParceledListSlice inputData,
+            int batchSize,
+            @InferenceInput.Params.ModelType int modelType,
+            @NonNull InferenceOutputParcel expectedOutputStructure) {
+        this.mModelId = modelId;
+        AnnotationValidations.validate(NonNull.class, null, mModelId);
+        this.mDelegate = delegate;
+        AnnotationValidations.validate(InferenceInput.Params.Delegate.class, null, mDelegate);
+        this.mCpuNumThread = cpuNumThread;
+        AnnotationValidations.validate(IntRange.class, null, mCpuNumThread, "from", 1);
+        this.mInputData = inputData;
+        AnnotationValidations.validate(NonNull.class, null, mInputData);
+        this.mBatchSize = batchSize;
+        this.mModelType = modelType;
+        AnnotationValidations.validate(InferenceInput.Params.ModelType.class, null, mModelType);
+        this.mExpectedOutputStructure = expectedOutputStructure;
+        AnnotationValidations.validate(NonNull.class, null, mExpectedOutputStructure);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The location of TFLite model. The model is usually store in REMOTE_DATA or LOCAL_DATA table.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ModelId getModelId() {
+        return mModelId;
+    }
+
+    /** The delegate to run model inference. If not specified, CPU delegate is used by default. */
+    @DataClass.Generated.Member
+    public @InferenceInput.Params.Delegate int getDelegate() {
+        return mDelegate;
+    }
+
+    /**
+     * The number of threads available to the interpreter. Only set and take effective when input
+     * tensors are on CPU. Setting cpuNumThread to 0 has the effect to disable multithreading, which
+     * is equivalent to setting cpuNumThread to 1. If set to the value -1, the number of threads
+     * used will be implementation-defined and platform-dependent.
+     */
+    @DataClass.Generated.Member
+    public @IntRange(from = 1) int getCpuNumThread() {
+        return mCpuNumThread;
+    }
+
+    /** An array of input data. The inputs should be in the same order as inputs of the model. */
+    @DataClass.Generated.Member
+    public @NonNull ByteArrayParceledListSlice getInputData() {
+        return mInputData;
+    }
+
+    /**
+     * The number of input examples. Adopter can set this field to run batching inference. The batch
+     * size is 1 by default.
+     */
+    @DataClass.Generated.Member
+    public int getBatchSize() {
+        return mBatchSize;
+    }
+
+    @DataClass.Generated.Member
+    public @InferenceInput.Params.ModelType int getModelType() {
+        return mModelType;
+    }
+
+    /**
+     * The empty InferenceOutput representing the expected output structure. For TFLite, the
+     * inference code will verify whether this expected output structure matches model output
+     * signature.
+     */
+    @DataClass.Generated.Member
+    public @NonNull InferenceOutputParcel getExpectedOutputStructure() {
+        return mExpectedOutputStructure;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeTypedObject(mModelId, flags);
+        dest.writeInt(mDelegate);
+        dest.writeInt(mCpuNumThread);
+        dest.writeTypedObject(mInputData, flags);
+        dest.writeInt(mBatchSize);
+        dest.writeInt(mModelType);
+        dest.writeTypedObject(mExpectedOutputStructure, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected InferenceInputParcel(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        ModelId modelId = (ModelId) in.readTypedObject(ModelId.CREATOR);
+        int delegate = in.readInt();
+        int cpuNumThread = in.readInt();
+        ByteArrayParceledListSlice inputData =
+                (ByteArrayParceledListSlice) in.readTypedObject(ByteArrayParceledListSlice.CREATOR);
+        int batchSize = in.readInt();
+        int modelType = in.readInt();
+        InferenceOutputParcel expectedOutputStructure =
+                (InferenceOutputParcel) in.readTypedObject(InferenceOutputParcel.CREATOR);
+
+        this.mModelId = modelId;
+        AnnotationValidations.validate(NonNull.class, null, mModelId);
+        this.mDelegate = delegate;
+        AnnotationValidations.validate(InferenceInput.Params.Delegate.class, null, mDelegate);
+        this.mCpuNumThread = cpuNumThread;
+        AnnotationValidations.validate(IntRange.class, null, mCpuNumThread, "from", 1);
+        this.mInputData = inputData;
+        AnnotationValidations.validate(NonNull.class, null, mInputData);
+        this.mBatchSize = batchSize;
+        this.mModelType = modelType;
+        AnnotationValidations.validate(InferenceInput.Params.ModelType.class, null, mModelType);
+        this.mExpectedOutputStructure = expectedOutputStructure;
+        AnnotationValidations.validate(NonNull.class, null, mExpectedOutputStructure);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<InferenceInputParcel> CREATOR =
+            new Parcelable.Creator<InferenceInputParcel>() {
+                @Override
+                public InferenceInputParcel[] newArray(int size) {
+                    return new InferenceInputParcel[size];
+                }
+
+                @Override
+                public InferenceInputParcel createFromParcel(@NonNull android.os.Parcel in) {
+                    return new InferenceInputParcel(in);
+                }
+            };
+
+    @DataClass.Generated(
+            time = 1708579683131L,
+            codegenVersion = "1.0.23",
+            sourceFile =
+                    "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/InferenceInputParcel.java",
+            inputSignatures =
+                    "private @android.annotation.NonNull android.adservices.ondevicepersonalization.ModelId mModelId\nprivate @android.adservices.ondevicepersonalization.InferenceInput.Params.Delegate int mDelegate\nprivate @android.annotation.IntRange int mCpuNumThread\nprivate @android.annotation.NonNull com.android.ondevicepersonalization.internal.util.ByteArrayParceledListSlice mInputData\nprivate  int mBatchSize\nprivate @android.adservices.ondevicepersonalization.InferenceInput.Params.ModelType int mModelType\nprivate @android.annotation.NonNull android.adservices.ondevicepersonalization.InferenceOutputParcel mExpectedOutputStructure\nclass InferenceInputParcel extends java.lang.Object implements [android.os.Parcelable]\[email protected](genAidl=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+    // @formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/InferenceOutput.java b/android-35/android/adservices/ondevicepersonalization/InferenceOutput.java
new file mode 100644
index 0000000..104a9ae
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/InferenceOutput.java
@@ -0,0 +1,170 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+import java.util.Collections;
+import java.util.Map;
+
+/** The result returned by {@link ModelManager#run}. */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public final class InferenceOutput {
+    /**
+     * A map mapping output indices to multidimensional arrays of output.
+     *
+     * <p>For TFLite, this field is mapped to outputs of runForMultipleInputsOutputs:
+     * https://www.tensorflow.org/lite/api_docs/java/org/tensorflow/lite/InterpreterApi#parameters_9
+     */
+    @NonNull private Map<Integer, Object> mDataOutputs = Collections.emptyMap();
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen
+    // $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/InferenceOutput.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    // @formatter:off
+
+    @DataClass.Generated.Member
+    /* package-private */ InferenceOutput(@NonNull Map<Integer, Object> dataOutputs) {
+        this.mDataOutputs = dataOutputs;
+        AnnotationValidations.validate(NonNull.class, null, mDataOutputs);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * A map mapping output indices to multidimensional arrays of output.
+     *
+     * <p>For TFLite, this field is mapped to outputs of runForMultipleInputsOutputs:
+     * https://www.tensorflow.org/lite/api_docs/java/org/tensorflow/lite/InterpreterApi#parameters_9
+     */
+    @DataClass.Generated.Member
+    public @NonNull Map<Integer, Object> getDataOutputs() {
+        return mDataOutputs;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(InferenceOutput other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        InferenceOutput that = (InferenceOutput) o;
+        //noinspection PointlessBooleanExpression
+        return true && java.util.Objects.equals(mDataOutputs, that.mDataOutputs);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mDataOutputs);
+        return _hash;
+    }
+
+    /** A builder for {@link InferenceOutput} */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @NonNull Map<Integer, Object> mDataOutputs;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {}
+
+        /**
+         * A map mapping output indices to multidimensional arrays of output.
+         *
+         * <p>For TFLite, this field is mapped to outputs of runForMultipleInputsOutputs:
+         * https://www.tensorflow.org/lite/api_docs/java/org/tensorflow/lite/InterpreterApi#parameters_9
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setDataOutputs(@NonNull Map<Integer, Object> value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mDataOutputs = value;
+            return this;
+        }
+
+        /**
+         * @see #setDataOutputs
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder addDataOutput(int key, @NonNull Object value) {
+            // You can refine this method's name by providing item's singular name, e.g.:
+            // @DataClass.PluralOf("item")) mItems = ...
+
+            if (mDataOutputs == null) setDataOutputs(new java.util.LinkedHashMap());
+            mDataOutputs.put(key, value);
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull InferenceOutput build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mDataOutputs = Collections.emptyMap();
+            }
+            InferenceOutput o = new InferenceOutput(mDataOutputs);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x2) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1707187954917L,
+            codegenVersion = "1.0.23",
+            sourceFile =
+                    "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/InferenceOutput.java",
+            inputSignatures =
+                    "private @android.annotation.NonNull java.util.Map<java.lang.Integer,java.lang.Object> mDataOutputs\nclass InferenceOutput extends java.lang.Object implements []\[email protected](genBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+    // @formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/InferenceOutputParcel.java b/android-35/android/adservices/ondevicepersonalization/InferenceOutputParcel.java
new file mode 100644
index 0000000..89f857b
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/InferenceOutputParcel.java
@@ -0,0 +1,143 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Parcelable version of {@link InferenceOutput}.
+ *
+ * @hide
+ */
+@DataClass(genAidl = false, genBuilder = false)
+public final class InferenceOutputParcel implements Parcelable {
+    /**
+     * A map mapping output indices to multidimensional arrays of output. For TFLite, this field is
+     * mapped to outputs of runForMultipleInputsOutputs:
+     * https://www.tensorflow.org/lite/api_docs/java/org/tensorflow/lite/InterpreterApi#parameters_9
+     */
+    @NonNull private Map<Integer, Object> mData = Collections.emptyMap();
+
+    /** @hide */
+    public InferenceOutputParcel(@NonNull InferenceOutput value) {
+        this(value.getDataOutputs());
+    }
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen
+    // $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/InferenceOutputParcel.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    // @formatter:off
+
+    /**
+     * Creates a new InferenceOutputParcel.
+     *
+     * @param data A map mapping output indices to multidimensional arrays of output. For TFLite,
+     *     this field is mapped to outputs of runForMultipleInputsOutputs:
+     *     https://www.tensorflow.org/lite/api_docs/java/org/tensorflow/lite/InterpreterApi#parameters_9
+     */
+    @DataClass.Generated.Member
+    public InferenceOutputParcel(@NonNull Map<Integer, Object> data) {
+        this.mData = data;
+        AnnotationValidations.validate(NonNull.class, null, mData);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * A map mapping output indices to multidimensional arrays of output. For TFLite, this field is
+     * mapped to outputs of runForMultipleInputsOutputs:
+     * https://www.tensorflow.org/lite/api_docs/java/org/tensorflow/lite/InterpreterApi#parameters_9
+     */
+    @DataClass.Generated.Member
+    public @NonNull Map<Integer, Object> getData() {
+        return mData;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeMap(mData);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected InferenceOutputParcel(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        Map<Integer, Object> data = new java.util.LinkedHashMap<>();
+        in.readMap(data, Object.class.getClassLoader());
+
+        this.mData = data;
+        AnnotationValidations.validate(NonNull.class, null, mData);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<InferenceOutputParcel> CREATOR =
+            new Parcelable.Creator<InferenceOutputParcel>() {
+                @Override
+                public InferenceOutputParcel[] newArray(int size) {
+                    return new InferenceOutputParcel[size];
+                }
+
+                @Override
+                public InferenceOutputParcel createFromParcel(@NonNull android.os.Parcel in) {
+                    return new InferenceOutputParcel(in);
+                }
+            };
+
+    @DataClass.Generated(
+            time = 1706291599206L,
+            codegenVersion = "1.0.23",
+            sourceFile =
+                    "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/InferenceOutputParcel.java",
+            inputSignatures =
+                    "private @android.annotation.NonNull java.util.Map<java.lang.Integer,java.lang.Object> mData\nclass InferenceOutputParcel extends java.lang.Object implements [android.os.Parcelable]\[email protected](genAidl=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+    // @formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/IsolatedService.java b/android-35/android/adservices/ondevicepersonalization/IsolatedService.java
new file mode 100644
index 0000000..13cc7c2
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/IsolatedService.java
@@ -0,0 +1,560 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.adservices.ondevicepersonalization.aidl.IDataAccessService;
+import android.adservices.ondevicepersonalization.aidl.IFederatedComputeService;
+import android.adservices.ondevicepersonalization.aidl.IIsolatedModelService;
+import android.adservices.ondevicepersonalization.aidl.IIsolatedService;
+import android.adservices.ondevicepersonalization.aidl.IIsolatedServiceCallback;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.OutcomeReceiver;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.SystemClock;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.LoggerFactory;
+import com.android.ondevicepersonalization.internal.util.OdpParceledListSlice;
+
+import java.util.Objects;
+import java.util.function.Function;
+
+// TODO(b/289102463): Add a link to the public ODP developer documentation.
+/**
+ * Base class for services that are started by ODP on a call to
+ * {@code OnDevicePersonalizationManager#execute(ComponentName, PersistableBundle,
+ * java.util.concurrent.Executor, OutcomeReceiver)}
+ * and run in an <a
+ * href="https://developer.android.com/guide/topics/manifest/service-element#isolated">isolated
+ * process</a>. The service can produce content to be displayed in a
+ * {@link android.view.SurfaceView} in a calling app and write persistent results to on-device
+ * storage, which can be consumed by Federated Analytics for cross-device statistical analysis or
+ * by Federated Learning for model training.
+ * Client apps use {@link OnDevicePersonalizationManager} to interact with an {@link
+ * IsolatedService}.
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public abstract class IsolatedService extends Service {
+    private static final String TAG = IsolatedService.class.getSimpleName();
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
+    private IBinder mBinder;
+
+    /** Creates a binder for an {@link IsolatedService}. */
+    @Override
+    public void onCreate() {
+        mBinder = new ServiceBinder();
+    }
+
+    /**
+     * Handles binding to the {@link IsolatedService}.
+     *
+     * @param intent The Intent that was used to bind to this service, as given to {@link
+     *     android.content.Context#bindService Context.bindService}. Note that any extras that were
+     *     included with the Intent at that point will <em>not</em> be seen here.
+     */
+    @Override
+    @Nullable
+    public IBinder onBind(@NonNull Intent intent) {
+        return mBinder;
+    }
+
+    /**
+     * Return an instance of an {@link IsolatedWorker} that handles client requests.
+     *
+     * @param requestToken an opaque token that identifies the current request to the service that
+     *     must be passed to service methods that depend on per-request state.
+     */
+    @NonNull
+    public abstract IsolatedWorker onRequest(@NonNull RequestToken requestToken);
+
+    /**
+     * Returns a Data Access Object for the REMOTE_DATA table. The REMOTE_DATA table is a read-only
+     * key-value store that contains data that is periodically downloaded from an endpoint declared
+     * in the <download> tag in the ODP manifest of the service, as shown in the following example.
+     *
+     * <pre>{@code
+     * <!-- Contents of res/xml/OdpSettings.xml -->
+     * <on-device-personalization>
+     * <!-- Name of the service subclass -->
+     * <service "com.example.odpsample.SampleService">
+     *   <!-- If this tag is present, ODP will periodically poll this URL and
+     *    download content to populate REMOTE_DATA. Adopters that do not need to
+     *    download content from their servers can skip this tag. -->
+     *   <download-settings url="https://example.com/get" />
+     * </service>
+     * </on-device-personalization>
+     * }</pre>
+     *
+     * @param requestToken an opaque token that identifies the current request to the service.
+     * @return A {@link KeyValueStore} object that provides access to the REMOTE_DATA table. The
+     *     methods in the returned {@link KeyValueStore} are blocking operations and should be
+     *     called from a worker thread and not the main thread or a binder thread.
+     * @see #onRequest(RequestToken)
+     */
+    @NonNull
+    public final KeyValueStore getRemoteData(@NonNull RequestToken requestToken) {
+        return new RemoteDataImpl(requestToken.getDataAccessService());
+    }
+
+    /**
+     * Returns a Data Access Object for the LOCAL_DATA table. The LOCAL_DATA table is a persistent
+     * key-value store that the service can use to store any data. The contents of this table are
+     * visible only to the service running in an isolated process and cannot be sent outside the
+     * device.
+     *
+     * @param requestToken an opaque token that identifies the current request to the service.
+     * @return A {@link MutableKeyValueStore} object that provides access to the LOCAL_DATA table.
+     *     The methods in the returned {@link MutableKeyValueStore} are blocking operations and
+     *     should be called from a worker thread and not the main thread or a binder thread.
+     * @see #onRequest(RequestToken)
+     */
+    @NonNull
+    public final MutableKeyValueStore getLocalData(@NonNull RequestToken requestToken) {
+        return new LocalDataImpl(requestToken.getDataAccessService());
+    }
+
+    /**
+     * Returns a DAO for the REQUESTS and EVENTS tables that provides
+     * access to the rows that are readable by the IsolatedService.
+     *
+     * @param requestToken an opaque token that identifies the current request to the service.
+     * @return A {@link LogReader} object that provides access to the REQUESTS and EVENTS table.
+     *     The methods in the returned {@link LogReader} are blocking operations and
+     *     should be called from a worker thread and not the main thread or a binder thread.
+     * @see #onRequest(RequestToken)
+     */
+    @NonNull
+    public final LogReader getLogReader(@NonNull RequestToken requestToken) {
+        return new LogReader(requestToken.getDataAccessService());
+    }
+
+    /**
+     * Returns an {@link EventUrlProvider} for the current request. The {@link EventUrlProvider}
+     * provides URLs that can be embedded in HTML. When the HTML is rendered in an
+     * {@link android.webkit.WebView}, the platform intercepts requests to these URLs and invokes
+     * {@code IsolatedWorker#onEvent(EventInput, Consumer)}.
+     *
+     * @param requestToken an opaque token that identifies the current request to the service.
+     * @return An {@link EventUrlProvider} that returns event tracking URLs.
+     * @see #onRequest(RequestToken)
+     */
+    @NonNull
+    public final EventUrlProvider getEventUrlProvider(@NonNull RequestToken requestToken) {
+        return new EventUrlProvider(requestToken.getDataAccessService());
+    }
+
+    /**
+     * Returns the platform-provided {@link UserData} for the current request.
+     *
+     * @param requestToken an opaque token that identifies the current request to the service.
+     * @return A {@link UserData} object.
+     * @see #onRequest(RequestToken)
+     */
+    @Nullable
+    public final UserData getUserData(@NonNull RequestToken requestToken) {
+        return requestToken.getUserData();
+    }
+
+    /**
+     * Returns an {@link FederatedComputeScheduler} for the current request. The {@link
+     * FederatedComputeScheduler} can be used to schedule and cancel federated computation jobs.
+     * The federated computation includes federated learning and federated analytic jobs.
+     *
+     * @param requestToken an opaque token that identifies the current request to the service.
+     * @return An {@link FederatedComputeScheduler} that returns a federated computation job
+     *     scheduler.
+     * @see #onRequest(RequestToken)
+     */
+    @NonNull
+    public final FederatedComputeScheduler getFederatedComputeScheduler(
+            @NonNull RequestToken requestToken) {
+        return new FederatedComputeScheduler(
+                requestToken.getFederatedComputeService(),
+                requestToken.getDataAccessService());
+    }
+
+    /**
+     * Returns an {@link ModelManager} for the current request. The {@link ModelManager} can be used
+     * to do model inference. It only supports Tensorflow Lite model inference now.
+     *
+     * @param requestToken an opaque token that identifies the current request to the service.
+     * @return An {@link ModelManager} that can be used for model inference.
+     */
+    @NonNull
+    public final ModelManager getModelManager(@NonNull RequestToken requestToken) {
+        return new ModelManager(
+                requestToken.getDataAccessService(), requestToken.getModelService());
+    }
+
+    // TODO(b/228200518): Add onBidRequest()/onBidResponse() methods.
+
+    class ServiceBinder extends IIsolatedService.Stub {
+        @Override
+        public void onRequest(
+                int operationCode,
+                @NonNull Bundle params,
+                @NonNull IIsolatedServiceCallback resultCallback) {
+            Objects.requireNonNull(params);
+            Objects.requireNonNull(resultCallback);
+            final long token = Binder.clearCallingIdentity();
+            // TODO(b/228200518): Ensure that caller is ODP Service.
+            // TODO(b/323592348): Add model inference in other flows.
+            try {
+                performRequest(operationCode, params, resultCallback);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        private void performRequest(
+                int operationCode,
+                @NonNull Bundle params,
+                @NonNull IIsolatedServiceCallback resultCallback) {
+
+            if (operationCode == Constants.OP_EXECUTE) {
+                performExecute(params, resultCallback);
+            } else if (operationCode == Constants.OP_DOWNLOAD) {
+                performDownload(params, resultCallback);
+            } else if (operationCode == Constants.OP_RENDER) {
+                performRender(params, resultCallback);
+            } else if (operationCode == Constants.OP_WEB_VIEW_EVENT) {
+                performOnWebViewEvent(params, resultCallback);
+            } else if (operationCode == Constants.OP_TRAINING_EXAMPLE) {
+                performOnTrainingExample(params, resultCallback);
+            } else if (operationCode == Constants.OP_WEB_TRIGGER) {
+                performOnWebTrigger(params, resultCallback);
+            } else {
+                throw new IllegalArgumentException("Invalid op code: " + operationCode);
+            }
+        }
+
+        private void performOnWebTrigger(
+                @NonNull Bundle params, @NonNull IIsolatedServiceCallback resultCallback) {
+            try {
+                WebTriggerInputParcel inputParcel =
+                        Objects.requireNonNull(
+                                params.getParcelable(
+                                        Constants.EXTRA_INPUT, WebTriggerInputParcel.class),
+                                () ->
+                                        String.format(
+                                                "Missing '%s' from input params!",
+                                                Constants.EXTRA_INPUT));
+                WebTriggerInput input = new WebTriggerInput(inputParcel);
+                IDataAccessService binder = getDataAccessService(params);
+                UserData userData = params.getParcelable(Constants.EXTRA_USER_DATA, UserData.class);
+                RequestToken requestToken = new RequestToken(binder, null, null, userData);
+                IsolatedWorker isolatedWorker = IsolatedService.this.onRequest(requestToken);
+                isolatedWorker.onWebTrigger(
+                        input,
+                        new WrappedCallback<WebTriggerOutput, WebTriggerOutputParcel>(
+                                resultCallback, requestToken, v -> new WebTriggerOutputParcel(v)));
+            } catch (Exception e) {
+                sLogger.e(e, TAG + ": Exception during Isolated Service web trigger operation.");
+                try {
+                    resultCallback.onError(Constants.STATUS_INTERNAL_ERROR, 0);
+                } catch (RemoteException re) {
+                    sLogger.e(re, TAG + ": Isolated Service Callback failed.");
+                }
+            }
+        }
+
+        private void performOnTrainingExample(
+                @NonNull Bundle params, @NonNull IIsolatedServiceCallback resultCallback) {
+            try {
+                TrainingExamplesInputParcel inputParcel =
+                        Objects.requireNonNull(
+                                params.getParcelable(
+                                        Constants.EXTRA_INPUT, TrainingExamplesInputParcel.class),
+                                () ->
+                                        String.format(
+                                                "Missing '%s' from input params!",
+                                                Constants.EXTRA_INPUT));
+                TrainingExamplesInput input = new TrainingExamplesInput(inputParcel);
+                IDataAccessService binder = getDataAccessService(params);
+                UserData userData = params.getParcelable(Constants.EXTRA_USER_DATA, UserData.class);
+                RequestToken requestToken = new RequestToken(binder, null, null, userData);
+                IsolatedWorker isolatedWorker = IsolatedService.this.onRequest(requestToken);
+                isolatedWorker.onTrainingExamples(
+                        input,
+                        new WrappedCallback<TrainingExamplesOutput, TrainingExamplesOutputParcel>(
+                                resultCallback,
+                                requestToken,
+                                v ->
+                                        new TrainingExamplesOutputParcel.Builder()
+                                                .setTrainingExampleRecords(
+                                                        new OdpParceledListSlice<
+                                                                TrainingExampleRecord>(
+                                                                v.getTrainingExampleRecords()))
+                                                .build()));
+            } catch (Exception e) {
+                sLogger.e(e,
+                        TAG + ": Exception during Isolated Service training example operation.");
+                try {
+                    resultCallback.onError(Constants.STATUS_INTERNAL_ERROR, 0);
+                } catch (RemoteException re) {
+                    sLogger.e(re, TAG + ": Isolated Service Callback failed.");
+                }
+            }
+        }
+
+        private void performOnWebViewEvent(
+                @NonNull Bundle params, @NonNull IIsolatedServiceCallback resultCallback) {
+            try {
+                EventInputParcel inputParcel =
+                        Objects.requireNonNull(
+                                params.getParcelable(Constants.EXTRA_INPUT, EventInputParcel.class),
+                                () ->
+                                        String.format(
+                                                "Missing '%s' from input params!",
+                                                Constants.EXTRA_INPUT));
+                EventInput input = new EventInput(inputParcel);
+                IDataAccessService binder = getDataAccessService(params);
+                UserData userData = params.getParcelable(Constants.EXTRA_USER_DATA, UserData.class);
+                RequestToken requestToken = new RequestToken(binder, null, null, userData);
+                IsolatedWorker isolatedWorker = IsolatedService.this.onRequest(requestToken);
+                isolatedWorker.onEvent(
+                        input,
+                        new WrappedCallback<EventOutput, EventOutputParcel>(
+                                resultCallback, requestToken, v -> new EventOutputParcel(v)));
+            } catch (Exception e) {
+                sLogger.e(e, TAG + ": Exception during Isolated Service web view event operation.");
+                try {
+                    resultCallback.onError(Constants.STATUS_INTERNAL_ERROR, 0);
+                } catch (RemoteException re) {
+                    sLogger.e(re, TAG + ": Isolated Service Callback failed.");
+                }
+            }
+        }
+
+        private void performRender(
+                @NonNull Bundle params, @NonNull IIsolatedServiceCallback resultCallback) {
+            try {
+                RenderInputParcel inputParcel =
+                        Objects.requireNonNull(
+                                params.getParcelable(
+                                        Constants.EXTRA_INPUT, RenderInputParcel.class),
+                                () ->
+                                        String.format(
+                                                "Missing '%s' from input params!",
+                                                Constants.EXTRA_INPUT));
+                RenderInput input = new RenderInput(inputParcel);
+                Objects.requireNonNull(input.getRenderingConfig());
+                IDataAccessService binder = getDataAccessService(params);
+                RequestToken requestToken = new RequestToken(binder, null, null, null);
+                IsolatedWorker isolatedWorker = IsolatedService.this.onRequest(requestToken);
+                isolatedWorker.onRender(
+                        input,
+                        new WrappedCallback<RenderOutput, RenderOutputParcel>(
+                                resultCallback, requestToken, v -> new RenderOutputParcel(v)));
+            } catch (Exception e) {
+                sLogger.e(e, TAG + ": Exception during Isolated Service render operation.");
+                try {
+                    resultCallback.onError(Constants.STATUS_INTERNAL_ERROR, 0);
+                } catch (RemoteException re) {
+                    sLogger.e(re, TAG + ": Isolated Service Callback failed.");
+                }
+            }
+        }
+
+        private void performDownload(
+                @NonNull Bundle params, @NonNull IIsolatedServiceCallback resultCallback) {
+            try {
+                DownloadInputParcel inputParcel =
+                        Objects.requireNonNull(
+                                params.getParcelable(
+                                        Constants.EXTRA_INPUT, DownloadInputParcel.class),
+                                () ->
+                                        String.format(
+                                                "Missing '%s' from input params!",
+                                                Constants.EXTRA_INPUT));
+                KeyValueStore downloadedContents =
+                        new RemoteDataImpl(
+                                IDataAccessService.Stub.asInterface(
+                                        Objects.requireNonNull(
+                                                inputParcel.getDataAccessServiceBinder(),
+                            "Failed to get IDataAccessService binder from the input params!")));
+
+                DownloadCompletedInput input =
+                        new DownloadCompletedInput.Builder()
+                                .setDownloadedContents(downloadedContents)
+                                .build();
+
+                IDataAccessService binder = getDataAccessService(params);
+
+                IFederatedComputeService fcBinder = getFederatedComputeService(params);
+                UserData userData = params.getParcelable(Constants.EXTRA_USER_DATA, UserData.class);
+                RequestToken requestToken = new RequestToken(binder, fcBinder, null, userData);
+                IsolatedWorker isolatedWorker = IsolatedService.this.onRequest(requestToken);
+                isolatedWorker.onDownloadCompleted(
+                        input,
+                        new WrappedCallback<DownloadCompletedOutput, DownloadCompletedOutputParcel>(
+                                resultCallback,
+                                requestToken,
+                                v -> new DownloadCompletedOutputParcel(v)));
+            } catch (Exception e) {
+                sLogger.e(e, TAG + ": Exception during Isolated Service download operation.");
+                try {
+                    resultCallback.onError(Constants.STATUS_INTERNAL_ERROR, 0);
+                } catch (RemoteException re) {
+                    sLogger.e(re, TAG + ": Isolated Service Callback failed.");
+                }
+            }
+        }
+
+        private static IIsolatedModelService getIsolatedModelService(@NonNull Bundle params) {
+            IIsolatedModelService modelServiceBinder =
+                    IIsolatedModelService.Stub.asInterface(
+                            Objects.requireNonNull(
+                                    params.getBinder(Constants.EXTRA_MODEL_SERVICE_BINDER),
+                                    () ->
+                                            String.format(
+                                                    "Missing '%s' from input params!",
+                                                    Constants.EXTRA_MODEL_SERVICE_BINDER)));
+            Objects.requireNonNull(
+                    modelServiceBinder,
+                    "Failed to get IIsolatedModelService binder from the input params!");
+            return modelServiceBinder;
+        }
+
+        private static IFederatedComputeService getFederatedComputeService(@NonNull Bundle params) {
+            IFederatedComputeService fcBinder =
+                    IFederatedComputeService.Stub.asInterface(
+                            Objects.requireNonNull(
+                                    params.getBinder(
+                                            Constants.EXTRA_FEDERATED_COMPUTE_SERVICE_BINDER),
+                                    () ->
+                                            String.format(
+                                                    "Missing '%s' from input params!",
+                                                    Constants
+                                                        .EXTRA_FEDERATED_COMPUTE_SERVICE_BINDER)));
+            Objects.requireNonNull(
+                    fcBinder,
+                    "Failed to get IFederatedComputeService binder from the input params!");
+            return fcBinder;
+        }
+
+        private static IDataAccessService getDataAccessService(@NonNull Bundle params) {
+            IDataAccessService binder =
+                    IDataAccessService.Stub.asInterface(
+                            Objects.requireNonNull(
+                                    params.getBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER),
+                                    () ->
+                                            String.format(
+                                                    "Missing '%s' from input params!",
+                                                    Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER)));
+            Objects.requireNonNull(
+                    binder, "Failed to get IDataAccessService binder from the input params!");
+            return binder;
+        }
+
+        private void performExecute(
+                @NonNull Bundle params, @NonNull IIsolatedServiceCallback resultCallback) {
+            try {
+                ExecuteInputParcel inputParcel =
+                        Objects.requireNonNull(
+                                params.getParcelable(
+                                        Constants.EXTRA_INPUT, ExecuteInputParcel.class),
+                                () ->
+                                        String.format(
+                                                "Missing '%s' from input params!",
+                                                Constants.EXTRA_INPUT));
+                ExecuteInput input = new ExecuteInput(inputParcel);
+                Objects.requireNonNull(
+                        input.getAppPackageName(),
+                        "Failed to get AppPackageName from the input params!");
+                IDataAccessService binder = getDataAccessService(params);
+                IFederatedComputeService fcBinder = getFederatedComputeService(params);
+                IIsolatedModelService modelServiceBinder = getIsolatedModelService(params);
+                UserData userData = params.getParcelable(Constants.EXTRA_USER_DATA, UserData.class);
+                RequestToken requestToken =
+                        new RequestToken(binder, fcBinder, modelServiceBinder, userData);
+                IsolatedWorker isolatedWorker = IsolatedService.this.onRequest(requestToken);
+                isolatedWorker.onExecute(
+                        input,
+                        new WrappedCallback<ExecuteOutput, ExecuteOutputParcel>(
+                                resultCallback, requestToken, v -> new ExecuteOutputParcel(v)));
+            } catch (Exception e) {
+                sLogger.e(e, TAG + ": Exception during Isolated Service execute operation.");
+                try {
+                    resultCallback.onError(Constants.STATUS_INTERNAL_ERROR, 0);
+                } catch (RemoteException re) {
+                    sLogger.e(re, TAG + ": Isolated Service Callback failed.");
+                }
+            }
+        }
+    }
+
+    private static class WrappedCallback<T, U extends Parcelable>
+                implements OutcomeReceiver<T, IsolatedServiceException> {
+        @NonNull private final IIsolatedServiceCallback mCallback;
+        @NonNull private final RequestToken mRequestToken;
+        @NonNull private final Function<T, U> mConverter;
+
+        WrappedCallback(
+                IIsolatedServiceCallback callback,
+                RequestToken requestToken,
+                Function<T, U> converter) {
+            mCallback = Objects.requireNonNull(callback);
+            mRequestToken = Objects.requireNonNull(requestToken);
+            mConverter = Objects.requireNonNull(converter);
+        }
+
+        @Override
+        public void onResult(T result) {
+            long elapsedTimeMillis =
+                    SystemClock.elapsedRealtime() - mRequestToken.getStartTimeMillis();
+            if (result == null) {
+                try {
+                    mCallback.onError(Constants.STATUS_SERVICE_FAILED, 0);
+                } catch (RemoteException e) {
+                    sLogger.w(TAG + ": Callback failed.", e);
+                }
+            } else {
+                Bundle bundle = new Bundle();
+                U wrappedResult = mConverter.apply(result);
+                bundle.putParcelable(Constants.EXTRA_RESULT, wrappedResult);
+                bundle.putParcelable(Constants.EXTRA_CALLEE_METADATA,
+                        new CalleeMetadata.Builder()
+                            .setElapsedTimeMillis(elapsedTimeMillis)
+                            .build());
+                try {
+                    mCallback.onSuccess(bundle);
+                } catch (RemoteException e) {
+                    sLogger.w(TAG + ": Callback failed.", e);
+                }
+            }
+        }
+
+        @Override
+        public void onError(IsolatedServiceException e) {
+            try {
+                // TODO(b/324478256): Log and report the error code from e.
+                mCallback.onError(Constants.STATUS_SERVICE_FAILED, e.getErrorCode());
+            } catch (RemoteException re) {
+                sLogger.w(TAG + ": Callback failed.", re);
+            }
+        }
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/IsolatedServiceException.java b/android-35/android/adservices/ondevicepersonalization/IsolatedServiceException.java
new file mode 100644
index 0000000..b280b10
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/IsolatedServiceException.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+
+/**
+ * A class that an {@link IsolatedService} can use to signal a failure in handling a request and
+ * return an error to be logged and aggregated. The error is not reported to the app that invoked
+ * the {@link IsolatedService} in order to prevent data leakage from the {@link IsolatedService} to
+ * an app. The platform does not interpret the error code, it only logs and aggregates it.
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public final class IsolatedServiceException extends Exception {
+    @IntRange(from = 1, to = 127) private final int mErrorCode;
+
+    /**
+     * Creates an {@link IsolatedServiceException} with an error code to be logged. The meaning of
+     * the error code is defined by the {@link IsolatedService}. The platform does not interpret
+     * the error code.
+     *
+     * @param errorCode An error code defined by the {@link IsolatedService}.
+     */
+    public IsolatedServiceException(@IntRange(from = 1, to = 127) int errorCode) {
+        super("IsolatedServiceException: Error " + errorCode);
+        mErrorCode = errorCode;
+    }
+
+    /**
+     * Returns the error code for this exception.
+     * @hide
+     */
+    public @IntRange(from = 1, to = 127) int getErrorCode() {
+        return mErrorCode;
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/IsolatedWorker.java b/android-35/android/adservices/ondevicepersonalization/IsolatedWorker.java
new file mode 100644
index 0000000..1b37b69
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/IsolatedWorker.java
@@ -0,0 +1,157 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.OutcomeReceiver;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+
+/**
+ * Interface with methods that need to be implemented to handle requests from the
+ * OnDevicePersonalization service to an {@link IsolatedService}. The {@link IsolatedService}
+ * creates an instance of {@link IsolatedWorker} on each request and calls one of the methods
+ * below, depending the type of the request. The {@link IsolatedService} calls the method on a
+ * Binder thread and the {@link IsolatedWorker} should offload long running operations to a
+ * worker thread. The {@link IsolatedWorker} should use the {@code receiver} parameter of each
+ * method to return results. If any of these methods throws a {@link RuntimeException}, the
+ * platform treats it as an unrecoverable error in the {@link IsolatedService} and ends processing
+ * the request.
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public interface IsolatedWorker {
+
+    /**
+     * Handles a request from an app. This method is called when an app calls {@code
+     * OnDevicePersonalizationManager#execute(ComponentName, PersistableBundle,
+     * java.util.concurrent.Executor, OutcomeReceiver)} that refers to a named
+     * {@link IsolatedService}.
+     *
+     * @param input Request Parameters from the calling app.
+     * @param receiver Callback that receives the result {@link ExecuteOutput} or an
+     *     {@link IsolatedServiceException}. If this method throws a {@link RuntimeException} or
+     *     returns either {@code null} or {@link IsolatedServiceException}, the error is indicated
+     *     to the calling app as an {@link OnDevicePersonalizationException} with error code
+     *     {@link OnDevicePersonalizationException#ERROR_ISOLATED_SERVICE_FAILED}. To avoid leaking
+     *     private data to the calling app, more detailed errors are not reported to the caller.
+     *     If the {@link IsolatedService} needs to report additional data beyond the error code to
+     *     its backend servers, it should populate the logging fields in {@link ExecuteOutput} with
+     *     the additional error data for logging, and rely on Federated Analytics for the stats.
+     */
+    default void onExecute(
+            @NonNull ExecuteInput input,
+            @NonNull OutcomeReceiver<ExecuteOutput, IsolatedServiceException> receiver) {
+        receiver.onResult(new ExecuteOutput.Builder().build());
+    }
+
+    /**
+     * Handles a completed download. The platform downloads content using the parameters defined in
+     * the package manifest of the {@link IsolatedService}, calls this function after the download
+     * is complete, and updates the REMOTE_DATA table from
+     * {@link IsolatedService#getRemoteData(RequestToken)} with the result of this method.
+     *
+     * @param input Download handler parameters.
+     * @param receiver Callback that receives the result {@link DownloadCompletedOutput} or an
+     *     {@link IsolatedServiceException}.
+     *     <p>If this method returns a {@code null} result or exception via the callback, or
+     *     throws a {@link RuntimeException}, no updates are made to the REMOTE_DATA table.
+     */
+    default void onDownloadCompleted(
+            @NonNull DownloadCompletedInput input,
+            @NonNull OutcomeReceiver<DownloadCompletedOutput, IsolatedServiceException> receiver) {
+        receiver.onResult(new DownloadCompletedOutput.Builder().build());
+    }
+
+    /**
+     * Generates HTML for the results that were returned as a result of
+     * {@link #onExecute(ExecuteInput, android.os.OutcomeReceiver)}. Called when a client app calls
+     * {@link OnDevicePersonalizationManager#requestSurfacePackage(SurfacePackageToken, IBinder, int, int, int, java.util.concurrent.Executor, OutcomeReceiver)}.
+     * The platform will render this HTML in an {@link android.webkit.WebView} inside a fenced
+     * frame.
+     *
+     * @param input Parameters for the render request.
+     * @param receiver Callback that receives the result {@link RenderOutput} or an
+     *     {@link IsolatedServiceException}.
+     *     <p>If this method returns a {@code null} result or exception via the callback, or
+     *     throws a {@link RuntimeException}, the error is also reported to calling
+     *     apps as an {@link OnDevicePersonalizationException} with error code {@link
+     *     OnDevicePersonalizationException#ERROR_ISOLATED_SERVICE_FAILED}.
+     */
+    default void onRender(
+            @NonNull RenderInput input,
+            @NonNull OutcomeReceiver<RenderOutput, IsolatedServiceException> receiver) {
+        receiver.onResult(new RenderOutput.Builder().build());
+    }
+
+    /**
+     * Handles an event triggered by a request to a platform-provided tracking URL {@link
+     * EventUrlProvider} that was embedded in the HTML output returned by
+     * {@link #onRender(RenderInput, android.os.OutcomeReceiver)}. The platform updates the EVENTS table with
+     * {@link EventOutput#getEventLogRecord()}.
+     *
+     * @param input The parameters needed to compute event data.
+     * @param receiver Callback that receives the result {@link EventOutput} or an
+     *     {@link IsolatedServiceException}.
+     *     <p>If this method returns a {@code null} result or exception via the callback, or
+     *     throws a {@link RuntimeException}, no data is written to the EVENTS table.
+     */
+    default void onEvent(
+            @NonNull EventInput input,
+            @NonNull OutcomeReceiver<EventOutput, IsolatedServiceException> receiver) {
+        receiver.onResult(new EventOutput.Builder().build());
+    }
+
+    /**
+     * Generate a list of training examples used for federated compute job. The platform will call
+     * this function when a federated compute job starts. The federated compute job is scheduled by
+     * an app through {@link FederatedComputeScheduler#schedule}.
+     *
+     * @param input The parameters needed to generate the training example.
+     * @param receiver Callback that receives the result {@link TrainingExamplesOutput} or an
+     *     {@link IsolatedServiceException}.
+     *     <p>If this method returns a {@code null} result or exception via the callback, or
+     *     throws a {@link RuntimeException}, no training examples is produced for this
+     *     training session.
+     */
+    default void onTrainingExamples(
+            @NonNull TrainingExamplesInput input,
+            @NonNull OutcomeReceiver<TrainingExamplesOutput, IsolatedServiceException> receiver) {
+        receiver.onResult(new TrainingExamplesOutput.Builder().build());
+    }
+
+    /**
+     * Handles a Web Trigger event from a browser. A Web Trigger event occurs when a browser
+     * registers a web trigger event with the OS using the <a href="https://github.com/WICG/attribution-reporting-api">
+     * Attribution and Reporting API</a>. If the data in the web trigger payload indicates that the
+     * event should be forwarded to an {@link IsolatedService}, the platform will call this function
+     * with the web trigger data.
+     *
+     * @param input The parameters needed to process Web Trigger event.
+     * @param receiver Callback that receives the result {@link WebTriggerOutput} or an
+     *     {@link IsolatedServiceException}. Should be called with a
+     *     {@link WebTriggerOutput} object populated with a set of records to be written to the
+     *     REQUESTS or EVENTS tables.
+     *     <p>If this method returns a {@code null} result or exception via the callback, or
+     *     throws a {@link RuntimeException}, no data is written to the REQUESTS orEVENTS tables.
+     */
+    default void onWebTrigger(
+            @NonNull WebTriggerInput input,
+            @NonNull OutcomeReceiver<WebTriggerOutput, IsolatedServiceException> receiver) {
+        receiver.onResult(new WebTriggerOutput.Builder().build());
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/JoinedLogRecord.java b/android-35/android/adservices/ondevicepersonalization/JoinedLogRecord.java
new file mode 100644
index 0000000..451c35a
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/JoinedLogRecord.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.IntRange;
+import android.annotation.Nullable;
+import android.content.ContentValues;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * Input data to create example from. Represents a single joined log record.
+ *
+ * @hide
+ */
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public class JoinedLogRecord implements Parcelable {
+    /** Time of the request in milliseconds */
+    private final long mRequestTimeMillis;
+
+    /** Time of the event in milliseconds */
+    private final long mEventTimeMillis;
+
+    /**
+     * The service-assigned type that identifies the event data. Must be >0 and <128. If type is 0,
+     * it is an Request-only row with no associated event.
+     */
+    @IntRange(from = 0, to = 127)
+    private final int mType;
+
+    /** Request data logged in a {@link RequestLogRecord} */
+    @Nullable private ContentValues mRequestData = null;
+
+    /** Event data logged in an {@link EventLogRecord} */
+    @Nullable private ContentValues mEventData = null;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/JoinedLogRecord.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ JoinedLogRecord(
+            long requestTimeMillis,
+            long eventTimeMillis,
+            @IntRange(from = 0, to = 127) int type,
+            @Nullable ContentValues requestData,
+            @Nullable ContentValues eventData) {
+        this.mRequestTimeMillis = requestTimeMillis;
+        this.mEventTimeMillis = eventTimeMillis;
+        this.mType = type;
+        AnnotationValidations.validate(
+                IntRange.class, null, mType,
+                "from", 0,
+                "to", 127);
+        this.mRequestData = requestData;
+        this.mEventData = eventData;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Time of the request in milliseconds
+     */
+    @DataClass.Generated.Member
+    public long getRequestTimeMillis() {
+        return mRequestTimeMillis;
+    }
+
+    /**
+     * Time of the event in milliseconds
+     */
+    @DataClass.Generated.Member
+    public long getEventTimeMillis() {
+        return mEventTimeMillis;
+    }
+
+    /**
+     * The service-assigned type that identifies the event data. Must be >0 and <128. If type is 0,
+     * it is an Request-only row with no associated event.
+     */
+    @DataClass.Generated.Member
+    public @IntRange(from = 0, to = 127) int getType() {
+        return mType;
+    }
+
+    /**
+     * Request data logged in a {@link RequestLogRecord}
+     */
+    @DataClass.Generated.Member
+    public @Nullable ContentValues getRequestData() {
+        return mRequestData;
+    }
+
+    /**
+     * Event data logged in an {@link EventLogRecord}
+     */
+    @DataClass.Generated.Member
+    public @Nullable ContentValues getEventData() {
+        return mEventData;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(JoinedLogRecord other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        JoinedLogRecord that = (JoinedLogRecord) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && mRequestTimeMillis == that.mRequestTimeMillis
+                && mEventTimeMillis == that.mEventTimeMillis
+                && mType == that.mType
+                && java.util.Objects.equals(mRequestData, that.mRequestData)
+                && java.util.Objects.equals(mEventData, that.mEventData);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + Long.hashCode(mRequestTimeMillis);
+        _hash = 31 * _hash + Long.hashCode(mEventTimeMillis);
+        _hash = 31 * _hash + mType;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mRequestData);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mEventData);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mRequestData != null) flg |= 0x8;
+        if (mEventData != null) flg |= 0x10;
+        dest.writeByte(flg);
+        dest.writeLong(mRequestTimeMillis);
+        dest.writeLong(mEventTimeMillis);
+        dest.writeInt(mType);
+        if (mRequestData != null) dest.writeTypedObject(mRequestData, flags);
+        if (mEventData != null) dest.writeTypedObject(mEventData, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected JoinedLogRecord(@android.annotation.NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        long requestTimeMillis = in.readLong();
+        long eventTimeMillis = in.readLong();
+        int type = in.readInt();
+        ContentValues requestData = (flg & 0x8) == 0 ? null : (ContentValues) in.readTypedObject(ContentValues.CREATOR);
+        ContentValues eventData = (flg & 0x10) == 0 ? null : (ContentValues) in.readTypedObject(ContentValues.CREATOR);
+
+        this.mRequestTimeMillis = requestTimeMillis;
+        this.mEventTimeMillis = eventTimeMillis;
+        this.mType = type;
+        AnnotationValidations.validate(
+                IntRange.class, null, mType,
+                "from", 0,
+                "to", 127);
+        this.mRequestData = requestData;
+        this.mEventData = eventData;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @android.annotation.NonNull Parcelable.Creator<JoinedLogRecord> CREATOR
+            = new Parcelable.Creator<JoinedLogRecord>() {
+        @Override
+        public JoinedLogRecord[] newArray(int size) {
+            return new JoinedLogRecord[size];
+        }
+
+        @Override
+        public JoinedLogRecord createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
+            return new JoinedLogRecord(in);
+        }
+    };
+
+    /**
+     * A builder for {@link JoinedLogRecord}
+     * @hide
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static class Builder {
+
+        private long mRequestTimeMillis;
+        private long mEventTimeMillis;
+        private @IntRange(from = 0, to = 127) int mType;
+        private @Nullable ContentValues mRequestData;
+        private @Nullable ContentValues mEventData;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * Creates a new Builder.
+         *
+         * @param requestTimeMillis
+         *   Time of the request in milliseconds
+         * @param eventTimeMillis
+         *   Time of the event in milliseconds
+         * @param type
+         *   The service-assigned type that identifies the event data. Must be >0 and <128. If type is 0,
+         *   it is an Request-only row with no associated event.
+         */
+        public Builder(
+                long requestTimeMillis,
+                long eventTimeMillis,
+                @IntRange(from = 0, to = 127) int type) {
+            mRequestTimeMillis = requestTimeMillis;
+            mEventTimeMillis = eventTimeMillis;
+            mType = type;
+            AnnotationValidations.validate(
+                    IntRange.class, null, mType,
+                    "from", 0,
+                    "to", 127);
+        }
+
+        /**
+         * Time of the request in milliseconds
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setRequestTimeMillis(long value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mRequestTimeMillis = value;
+            return this;
+        }
+
+        /**
+         * Time of the event in milliseconds
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setEventTimeMillis(long value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mEventTimeMillis = value;
+            return this;
+        }
+
+        /**
+         * The service-assigned type that identifies the event data. Must be >0 and <128. If type is 0,
+         * it is an Request-only row with no associated event.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setType(@IntRange(from = 0, to = 127) int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mType = value;
+            return this;
+        }
+
+        /**
+         * Request data logged in a {@link RequestLogRecord}
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setRequestData(@android.annotation.NonNull ContentValues value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mRequestData = value;
+            return this;
+        }
+
+        /**
+         * Event data logged in an {@link EventLogRecord}
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setEventData(@android.annotation.NonNull ContentValues value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10;
+            mEventData = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @android.annotation.NonNull JoinedLogRecord build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x20; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x8) == 0) {
+                mRequestData = null;
+            }
+            if ((mBuilderFieldsSet & 0x10) == 0) {
+                mEventData = null;
+            }
+            JoinedLogRecord o = new JoinedLogRecord(
+                    mRequestTimeMillis,
+                    mEventTimeMillis,
+                    mType,
+                    mRequestData,
+                    mEventData);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x20) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1695413878624L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/JoinedLogRecord.java",
+            inputSignatures = "private final  long mRequestTimeMillis\nprivate final  long mEventTimeMillis\nprivate final @android.annotation.IntRange int mType\nprivate @android.annotation.Nullable android.content.ContentValues mRequestData\nprivate @android.annotation.Nullable android.content.ContentValues mEventData\nclass JoinedLogRecord extends java.lang.Object implements [android.os.Parcelable]\[email protected](genBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/KeyValueStore.java b/android-35/android/adservices/ondevicepersonalization/KeyValueStore.java
new file mode 100644
index 0000000..198af7c
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/KeyValueStore.java
@@ -0,0 +1,63 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+
+import java.util.Set;
+
+/**
+ * An interface to a read-only key-value store.
+ *
+ * Used as a Data Access Object for the REMOTE_DATA table.
+ *
+ * @see IsolatedService#getRemoteData(RequestToken)
+ *
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public interface KeyValueStore {
+    /**
+     * Looks up a key in a read-only store.
+     *
+     * @param key The key to look up.
+     * @return the value to which the specified key is mapped,
+     * or null if there contains no mapping for the key.
+     *
+     */
+    @WorkerThread
+    @Nullable byte[] get(@NonNull String key);
+
+    /**
+     * Returns a Set view of the keys contained in the REMOTE_DATA table.
+     */
+    @WorkerThread
+    @NonNull Set<String> keySet();
+
+    /**
+     * Returns the table id {@link ModelId.TableId} of KeyValueStore implementation.
+     *
+     * @hide
+     */
+    default int getTableId(){
+        return 0;
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/LocalDataImpl.java b/android-35/android/adservices/ondevicepersonalization/LocalDataImpl.java
new file mode 100644
index 0000000..e09c4c4
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/LocalDataImpl.java
@@ -0,0 +1,168 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.adservices.ondevicepersonalization.aidl.IDataAccessService;
+import android.adservices.ondevicepersonalization.aidl.IDataAccessServiceCallback;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import com.android.ondevicepersonalization.internal.util.ByteArrayParceledSlice;
+import com.android.ondevicepersonalization.internal.util.LoggerFactory;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+/** @hide */
+public class LocalDataImpl implements MutableKeyValueStore {
+    private static final String TAG = "LocalDataImpl";
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
+    @NonNull
+    IDataAccessService mDataAccessService;
+
+    /** @hide */
+    public LocalDataImpl(@NonNull IDataAccessService binder) {
+        mDataAccessService = Objects.requireNonNull(binder);
+    }
+
+    @Override @Nullable
+    public byte[] get(@NonNull String key) {
+        final long startTimeMillis = System.currentTimeMillis();
+        Objects.requireNonNull(key);
+        Bundle params = new Bundle();
+        params.putString(Constants.EXTRA_LOOKUP_KEYS, key);
+        return handleLookupRequest(
+                Constants.DATA_ACCESS_OP_LOCAL_DATA_LOOKUP, params,
+                Constants.API_NAME_LOCAL_DATA_GET, startTimeMillis);
+    }
+
+    @Override @Nullable
+    public byte[] put(@NonNull String key, byte[] value) {
+        final long startTimeMillis = System.currentTimeMillis();
+        Objects.requireNonNull(key);
+        Bundle params = new Bundle();
+        params.putString(Constants.EXTRA_LOOKUP_KEYS, key);
+        params.putParcelable(Constants.EXTRA_VALUE, new ByteArrayParceledSlice(value));
+        return handleLookupRequest(
+                Constants.DATA_ACCESS_OP_LOCAL_DATA_PUT, params,
+                Constants.API_NAME_LOCAL_DATA_PUT, startTimeMillis);
+    }
+
+    @Override @Nullable
+    public byte[] remove(@NonNull String key) {
+        final long startTimeMillis = System.currentTimeMillis();
+        Objects.requireNonNull(key);
+        Bundle params = new Bundle();
+        params.putString(Constants.EXTRA_LOOKUP_KEYS, key);
+        return handleLookupRequest(
+                Constants.DATA_ACCESS_OP_LOCAL_DATA_REMOVE, params,
+                Constants.API_NAME_LOCAL_DATA_REMOVE, startTimeMillis);
+    }
+
+    private byte[] handleLookupRequest(
+            int op, Bundle params, int apiName, long startTimeMillis) {
+        int responseCode = Constants.STATUS_SUCCESS;
+        try {
+            Bundle result = handleAsyncRequest(op, params);
+            ByteArrayParceledSlice data = result.getParcelable(
+                    Constants.EXTRA_RESULT, ByteArrayParceledSlice.class);
+            if (null == data) {
+                return null;
+            }
+            return data.getByteArray();
+        } catch (RuntimeException e) {
+            responseCode = Constants.STATUS_INTERNAL_ERROR;
+            throw e;
+        } finally {
+            try {
+                mDataAccessService.logApiCallStats(
+                        apiName,
+                        System.currentTimeMillis() - startTimeMillis,
+                        responseCode);
+            } catch (Exception e) {
+                sLogger.d(e, TAG + ": failed to log metrics");
+            }
+        }
+    }
+
+    @Override @NonNull
+    public Set<String> keySet() {
+        final long startTimeMillis = System.currentTimeMillis();
+        int responseCode = Constants.STATUS_SUCCESS;
+        try {
+            Bundle result = handleAsyncRequest(Constants.DATA_ACCESS_OP_LOCAL_DATA_KEYSET,
+                    Bundle.EMPTY);
+            HashSet<String> resultSet =
+                    result.getSerializable(Constants.EXTRA_RESULT, HashSet.class);
+            if (null == resultSet) {
+                return Collections.emptySet();
+            }
+            return resultSet;
+        } catch (RuntimeException e) {
+            responseCode = Constants.STATUS_INTERNAL_ERROR;
+            throw e;
+        } finally {
+            try {
+                mDataAccessService.logApiCallStats(
+                        Constants.API_NAME_LOCAL_DATA_KEYSET,
+                        System.currentTimeMillis() - startTimeMillis,
+                        responseCode);
+            } catch (Exception e) {
+                sLogger.d(e, TAG + ": failed to log metrics");
+            }
+        }
+    }
+
+    @Override
+    public int getTableId() {
+        return ModelId.TABLE_ID_LOCAL_DATA;
+    }
+
+    private Bundle handleAsyncRequest(int op, Bundle params) {
+        try {
+            BlockingQueue<Bundle> asyncResult = new ArrayBlockingQueue<>(1);
+            mDataAccessService.onRequest(
+                    op,
+                    params,
+                    new IDataAccessServiceCallback.Stub() {
+                        @Override
+                        public void onSuccess(@NonNull Bundle result) {
+                            if (result != null) {
+                                asyncResult.add(result);
+                            } else {
+                                asyncResult.add(Bundle.EMPTY);
+                            }
+                        }
+
+                        @Override
+                        public void onError(int errorCode) {
+                            asyncResult.add(Bundle.EMPTY);
+                        }
+                    });
+            return asyncResult.take();
+        } catch (InterruptedException | RemoteException e) {
+            sLogger.e(TAG + ": Failed to retrieve result from localData", e);
+            throw new IllegalStateException(e);
+        }
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/LogReader.java b/android-35/android/adservices/ondevicepersonalization/LogReader.java
new file mode 100644
index 0000000..04b603e
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/LogReader.java
@@ -0,0 +1,185 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.adservices.ondevicepersonalization.aidl.IDataAccessService;
+import android.adservices.ondevicepersonalization.aidl.IDataAccessServiceCallback;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.LoggerFactory;
+import com.android.ondevicepersonalization.internal.util.OdpParceledListSlice;
+
+import java.time.Instant;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+/**
+ * An interface to a read logs from REQUESTS and EVENTS
+ *
+ * Used as a Data Access Object for the REQUESTS and EVENTS table.
+ *
+ * @see IsolatedService#getLogReader(RequestToken)
+ *
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public class LogReader {
+    private static final String TAG = "LogReader";
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
+
+    @NonNull
+    private final IDataAccessService mDataAccessService;
+
+    /** @hide */
+    public LogReader(@NonNull IDataAccessService binder) {
+        mDataAccessService = Objects.requireNonNull(binder);
+    }
+
+
+    /**
+     * Retrieves a List of RequestLogRecords written by this IsolatedService within
+     * the specified time range.
+     */
+    @WorkerThread
+    @NonNull
+    public List<RequestLogRecord> getRequests(
+            @NonNull Instant startTime, @NonNull Instant endTime) {
+        final long apiStartTimeMillis = System.currentTimeMillis();
+        int responseCode = Constants.STATUS_SUCCESS;
+        long startTimeMillis = startTime.toEpochMilli();
+        long endTimeMillis = endTime.toEpochMilli();
+        if (endTimeMillis <= startTimeMillis) {
+            throw new IllegalArgumentException(
+                    "endTimeMillis must be greater than startTimeMillis");
+        }
+        if (startTimeMillis < 0) {
+            throw new IllegalArgumentException("startTimeMillis must be greater than 0");
+        }
+        try {
+            Bundle params = new Bundle();
+            params.putLongArray(Constants.EXTRA_LOOKUP_KEYS,
+                    new long[]{startTimeMillis, endTimeMillis});
+            OdpParceledListSlice<RequestLogRecord> result =
+                    handleListLookupRequest(Constants.DATA_ACCESS_OP_GET_REQUESTS, params);
+            return result.getList();
+        } catch (RuntimeException e) {
+            responseCode = Constants.STATUS_INTERNAL_ERROR;
+            throw e;
+        } finally {
+            logApiCallStats(
+                    Constants.API_NAME_LOG_READER_GET_REQUESTS,
+                    System.currentTimeMillis() - apiStartTimeMillis,
+                    responseCode);
+        }
+    }
+
+    /**
+     * Retrieves a List of EventLogRecord with its corresponding RequestLogRecord written by this
+     * IsolatedService within the specified time range.
+     */
+    @WorkerThread
+    @NonNull
+    public List<EventLogRecord> getJoinedEvents(
+            @NonNull Instant startTime, @NonNull Instant endTime) {
+        final long apiStartTimeMillis = System.currentTimeMillis();
+        int responseCode = Constants.STATUS_SUCCESS;
+        long startTimeMillis = startTime.toEpochMilli();
+        long endTimeMillis = endTime.toEpochMilli();
+        if (endTimeMillis <= startTimeMillis) {
+            throw new IllegalArgumentException(
+                    "endTimeMillis must be greater than startTimeMillis");
+        }
+        if (startTimeMillis < 0) {
+            throw new IllegalArgumentException("startTimeMillis must be greater than 0");
+        }
+        try {
+            Bundle params = new Bundle();
+            params.putLongArray(Constants.EXTRA_LOOKUP_KEYS,
+                    new long[]{startTimeMillis, endTimeMillis});
+            OdpParceledListSlice<EventLogRecord> result =
+                    handleListLookupRequest(Constants.DATA_ACCESS_OP_GET_JOINED_EVENTS, params);
+            return result.getList();
+        } catch (RuntimeException e) {
+            responseCode = Constants.STATUS_INTERNAL_ERROR;
+            throw e;
+        } finally {
+            logApiCallStats(
+                    Constants.API_NAME_LOG_READER_GET_JOINED_EVENTS,
+                    System.currentTimeMillis() - apiStartTimeMillis,
+                    responseCode);
+        }
+    }
+
+    private Bundle handleAsyncRequest(int op, Bundle params) {
+        try {
+            BlockingQueue<Bundle> asyncResult = new ArrayBlockingQueue<>(1);
+            mDataAccessService.onRequest(
+                    op,
+                    params,
+                    new IDataAccessServiceCallback.Stub() {
+                        @Override
+                        public void onSuccess(@NonNull Bundle result) {
+                            if (result != null) {
+                                asyncResult.add(result);
+                            } else {
+                                asyncResult.add(Bundle.EMPTY);
+                            }
+                        }
+
+                        @Override
+                        public void onError(int errorCode) {
+                            asyncResult.add(Bundle.EMPTY);
+                        }
+                    });
+            return asyncResult.take();
+        } catch (InterruptedException | RemoteException e) {
+            sLogger.e(TAG + ": Failed to retrieve result", e);
+            throw new IllegalStateException(e);
+        }
+    }
+
+    private <T extends Parcelable> OdpParceledListSlice<T> handleListLookupRequest(int op,
+            Bundle params) {
+        Bundle result = handleAsyncRequest(op, params);
+        try {
+            OdpParceledListSlice<T> data = result.getParcelable(
+                    Constants.EXTRA_RESULT, OdpParceledListSlice.class);
+            if (null == data) {
+                sLogger.e(TAG + ": No EXTRA_RESULT was present in bundle");
+                throw new IllegalStateException("Bundle missing EXTRA_RESULT.");
+            }
+            return data;
+        } catch (ClassCastException e) {
+            throw new IllegalStateException("Failed to retrieve parceled list");
+        }
+    }
+
+    private void logApiCallStats(int apiName, long duration, int responseCode) {
+        try {
+            mDataAccessService.logApiCallStats(apiName, duration, responseCode);
+        } catch (Exception e) {
+            sLogger.d(e, TAG + ": failed to log metrics");
+        }
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/MeasurementWebTriggerEventParams.java b/android-35/android/adservices/ondevicepersonalization/MeasurementWebTriggerEventParams.java
new file mode 100644
index 0000000..118c422
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/MeasurementWebTriggerEventParams.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.net.Uri;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+// TODO(b/301732670): Add link to documentation describing the format of the ODP-specific
+// attribution data that the server is expected to return.
+/**
+ * A class that contains Web Trigger Event data sent from the
+ * <a href="https://developer.android.com/design-for-safety/privacy-sandbox/guides/attribution">
+ * Measurement API</a> to the OnDevicePersonalization service when the browser registers a web
+ * trigger URL with the native OS attribution API as described in
+ * <a href="https://github.com/WICG/attribution-reporting-api/blob/main/app_to_web.md">
+ * Cross App and Web Attribution Measurement</a>. The Measurement API fetches and processes the
+ * attribution response from the browser-provided URL. If the URL response contains additional
+ * data that needs to be processed by an {@link IsolatedService}, the Measurement API passes this
+ * to the OnDevicePersonalization service and the OnDevicePersonalization service will invoke
+ * the {@link IsolatedService} with the provided data.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public final class MeasurementWebTriggerEventParams {
+    /**
+     * The URL of the web page where the web trigger event occurred.
+     */
+    @NonNull private Uri mDestinationUrl;
+
+    /**
+     * The package name of the browser app where the web trigger event occurred.
+     */
+    @NonNull private String mAppPackageName;
+
+    /**
+     * The package and class name of the {@link IsolatedService} that should process
+     * the web trigger event.
+     */
+    @NonNull private ComponentName mIsolatedService;
+
+    /**
+     * An optional SHA-256 hash of the signing key of the package that contains
+     * the {@link IsolatedService}, to guard against package name spoofing via sideloading.
+     * If this field is present and does not match the signing key of the installed receiver
+     * service package, the web trigger event is discarded.
+     */
+    @DataClass.MaySetToNull
+    @Nullable private String mCertDigest = null;
+
+    /**
+     * Additional data that the server may provide to the {@link IsolatedService}. This can be
+     * {@code null} if the server does not need to provide any data other than the required fields.
+     */
+    @DataClass.MaySetToNull
+    @Nullable private byte[] mEventData = null;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/MeasurementWebTriggerEventParams.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ MeasurementWebTriggerEventParams(
+            @NonNull Uri destinationUrl,
+            @NonNull String appPackageName,
+            @NonNull ComponentName isolatedService,
+            @Nullable String certDigest,
+            @Nullable byte[] eventData) {
+        this.mDestinationUrl = destinationUrl;
+        AnnotationValidations.validate(
+                NonNull.class, null, mDestinationUrl);
+        this.mAppPackageName = appPackageName;
+        AnnotationValidations.validate(
+                NonNull.class, null, mAppPackageName);
+        this.mIsolatedService = isolatedService;
+        AnnotationValidations.validate(
+                NonNull.class, null, mIsolatedService);
+        this.mCertDigest = certDigest;
+        this.mEventData = eventData;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The URL of the web page where the web trigger event occurred.
+     */
+    @DataClass.Generated.Member
+    public @NonNull Uri getDestinationUrl() {
+        return mDestinationUrl;
+    }
+
+    /**
+     * The package name of the browser app where the web trigger event occurred.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getAppPackageName() {
+        return mAppPackageName;
+    }
+
+    /**
+     * The package and class name of the {@link IsolatedService} that should process
+     * the web trigger event.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ComponentName getIsolatedService() {
+        return mIsolatedService;
+    }
+
+    /**
+     * An optional SHA-256 hash of the signing key of the package that contains
+     * the {@link IsolatedService}, to guard against package name spoofing via sideloading.
+     * If this field is present and does not match the signing key of the installed receiver
+     * service package, the web trigger event is discarded.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getCertDigest() {
+        return mCertDigest;
+    }
+
+    /**
+     * Additional data that the server may provide to the {@link IsolatedService}. This can be
+     * {@code null} if the server does not need to provide any data other than the required fields.
+     */
+    @DataClass.Generated.Member
+    public @Nullable byte[] getEventData() {
+        return mEventData;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(MeasurementWebTriggerEventParams other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        MeasurementWebTriggerEventParams that = (MeasurementWebTriggerEventParams) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mDestinationUrl, that.mDestinationUrl)
+                && java.util.Objects.equals(mAppPackageName, that.mAppPackageName)
+                && java.util.Objects.equals(mIsolatedService, that.mIsolatedService)
+                && java.util.Objects.equals(mCertDigest, that.mCertDigest)
+                && java.util.Arrays.equals(mEventData, that.mEventData);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mDestinationUrl);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mAppPackageName);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mIsolatedService);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mCertDigest);
+        _hash = 31 * _hash + java.util.Arrays.hashCode(mEventData);
+        return _hash;
+    }
+
+    /**
+     * A builder for {@link MeasurementWebTriggerEventParams}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @NonNull Uri mDestinationUrl;
+        private @NonNull String mAppPackageName;
+        private @NonNull ComponentName mIsolatedService;
+        private @Nullable String mCertDigest;
+        private @Nullable byte[] mEventData;
+
+        private long mBuilderFieldsSet = 0L;
+
+        /**
+         * Creates a new Builder.
+         *
+         * @param destinationUrl
+         *   The URL of the web page where the web trigger event occurred.
+         * @param appPackageName
+         *   The package name of the browser app where the web trigger event occurred.
+         * @param isolatedService
+         *   The package and class name of the {@link IsolatedService} that should process
+         *   the web trigger event.
+         */
+        public Builder(
+                @NonNull Uri destinationUrl,
+                @NonNull String appPackageName,
+                @NonNull ComponentName isolatedService) {
+            mDestinationUrl = destinationUrl;
+            AnnotationValidations.validate(
+                    NonNull.class, null, mDestinationUrl);
+            mAppPackageName = appPackageName;
+            AnnotationValidations.validate(
+                    NonNull.class, null, mAppPackageName);
+            mIsolatedService = isolatedService;
+            AnnotationValidations.validate(
+                    NonNull.class, null, mIsolatedService);
+        }
+
+        /**
+         * The URL of the web page where the web trigger event occurred.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setDestinationUrl(@NonNull Uri value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mDestinationUrl = value;
+            return this;
+        }
+
+        /**
+         * The package name of the browser app where the web trigger event occurred.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setAppPackageName(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mAppPackageName = value;
+            return this;
+        }
+
+        /**
+         * The package and class name of the {@link IsolatedService} that should process
+         * the web trigger event.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setIsolatedService(@NonNull ComponentName value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mIsolatedService = value;
+            return this;
+        }
+
+        /**
+         * An optional SHA-256 hash of the signing key of the package that contains
+         * the {@link IsolatedService}, to guard against package name spoofing via sideloading.
+         * If this field is present and does not match the signing key of the installed receiver
+         * service package, the web trigger event is discarded.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setCertDigest(@Nullable String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mCertDigest = value;
+            return this;
+        }
+
+        /**
+         * Additional data that the server may provide to the {@link IsolatedService}. This can be
+         * {@code null} if the server does not need to provide any data other than the required fields.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setEventData(@Nullable byte... value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10;
+            mEventData = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull MeasurementWebTriggerEventParams build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x20; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x8) == 0) {
+                mCertDigest = null;
+            }
+            if ((mBuilderFieldsSet & 0x10) == 0) {
+                mEventData = null;
+            }
+            MeasurementWebTriggerEventParams o = new MeasurementWebTriggerEventParams(
+                    mDestinationUrl,
+                    mAppPackageName,
+                    mIsolatedService,
+                    mCertDigest,
+                    mEventData);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x20) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1707510203588L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/MeasurementWebTriggerEventParams.java",
+            inputSignatures = "private @android.annotation.NonNull android.net.Uri mDestinationUrl\nprivate @android.annotation.NonNull java.lang.String mAppPackageName\nprivate @android.annotation.NonNull android.content.ComponentName mIsolatedService\nprivate @com.android.ondevicepersonalization.internal.util.DataClass.MaySetToNull @android.annotation.Nullable java.lang.String mCertDigest\nprivate @com.android.ondevicepersonalization.internal.util.DataClass.MaySetToNull @android.annotation.Nullable byte[] mEventData\nclass MeasurementWebTriggerEventParams extends java.lang.Object implements []\[email protected](genBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/MeasurementWebTriggerEventParamsParcel.java b/android-35/android/adservices/ondevicepersonalization/MeasurementWebTriggerEventParamsParcel.java
new file mode 100644
index 0000000..03f5eae
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/MeasurementWebTriggerEventParamsParcel.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.net.Uri;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * A class that contains Web Trigger Event data sent from the Measurement API to the
+ * OnDevicePersonalization service when the browser registers a web trigger
+ * with the <a href="https://developer.android.com/design-for-safety/privacy-sandbox/guides/attribution">
+ * Measurement API</a> and the web trigger data is intended to be processed by an
+ * {@link IsolatedService}.
+ * @hide
+ */
+@DataClass(genAidl = false, genBuilder = false)
+public final class MeasurementWebTriggerEventParamsParcel implements Parcelable {
+    /**
+     * The URL of the web page where the web trigger event occurred.
+     */
+    @NonNull private Uri mDestinationUrl;
+
+    /**
+     * The package name of the browser app where the web trigger event occurred.
+     */
+    @NonNull private String mAppPackageName;
+
+    /**
+     * The package and class name of the {@link IsolatedService} that should process
+     * the web trigger event.
+     */
+    @NonNull private ComponentName mIsolatedService;
+
+    /**
+     * An optional SHA-256 hash of the signing key of the package that contains
+     * the {@link IsolatedService}, to guard against package name spoofing via sideloading.
+     * If this field is present and does not match the signing key of the installed receiver
+     * service package, the web trigger event is discarded.
+     */
+    @Nullable private String mCertDigest = null;
+
+    /**
+     * Additional data that the server may provide to the {@link IsolatedService}. This can be
+     * {@code null} if the server does not need to provide any data other than the required fields.
+     */
+    @Nullable private byte[] mEventData = null;
+
+    public MeasurementWebTriggerEventParamsParcel(
+            @NonNull MeasurementWebTriggerEventParams params) {
+        this(params.getDestinationUrl(), params.getAppPackageName(), params.getIsolatedService(),
+                params.getCertDigest(), params.getEventData());
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/MeasurementWebTriggerEventParamsParcel.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new MeasurementWebTriggerEventParamsParcel.
+     *
+     * @param destinationUrl
+     *   The URL of the web page where the web trigger event occurred.
+     * @param appPackageName
+     *   The package name of the browser app where the web trigger event occurred.
+     * @param isolatedService
+     *   The package and class name of the {@link IsolatedService} that should process
+     *   the web trigger event.
+     * @param certDigest
+     *   An optional SHA-256 hash of the signing key of the package that contains
+     *   the {@link IsolatedService}, to guard against package name spoofing via sideloading.
+     *   If this field is present and does not match the signing key of the installed receiver
+     *   service package, the web trigger event is discarded.
+     * @param eventData
+     *   Additional data that the server may provide to the {@link IsolatedService}. This can be
+     *   {@code null} if the server does not need to provide any data other than the required fields.
+     */
+    @DataClass.Generated.Member
+    public MeasurementWebTriggerEventParamsParcel(
+            @NonNull Uri destinationUrl,
+            @NonNull String appPackageName,
+            @NonNull ComponentName isolatedService,
+            @Nullable String certDigest,
+            @Nullable byte[] eventData) {
+        this.mDestinationUrl = destinationUrl;
+        AnnotationValidations.validate(
+                NonNull.class, null, mDestinationUrl);
+        this.mAppPackageName = appPackageName;
+        AnnotationValidations.validate(
+                NonNull.class, null, mAppPackageName);
+        this.mIsolatedService = isolatedService;
+        AnnotationValidations.validate(
+                NonNull.class, null, mIsolatedService);
+        this.mCertDigest = certDigest;
+        this.mEventData = eventData;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The URL of the web page where the web trigger event occurred.
+     */
+    @DataClass.Generated.Member
+    public @NonNull Uri getDestinationUrl() {
+        return mDestinationUrl;
+    }
+
+    /**
+     * The package name of the browser app where the web trigger event occurred.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getAppPackageName() {
+        return mAppPackageName;
+    }
+
+    /**
+     * The package and class name of the {@link IsolatedService} that should process
+     * the web trigger event.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ComponentName getIsolatedService() {
+        return mIsolatedService;
+    }
+
+    /**
+     * An optional SHA-256 hash of the signing key of the package that contains
+     * the {@link IsolatedService}, to guard against package name spoofing via sideloading.
+     * If this field is present and does not match the signing key of the installed receiver
+     * service package, the web trigger event is discarded.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getCertDigest() {
+        return mCertDigest;
+    }
+
+    /**
+     * Additional data that the server may provide to the {@link IsolatedService}. This can be
+     * {@code null} if the server does not need to provide any data other than the required fields.
+     */
+    @DataClass.Generated.Member
+    public @Nullable byte[] getEventData() {
+        return mEventData;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mCertDigest != null) flg |= 0x8;
+        dest.writeByte(flg);
+        dest.writeTypedObject(mDestinationUrl, flags);
+        dest.writeString(mAppPackageName);
+        dest.writeTypedObject(mIsolatedService, flags);
+        if (mCertDigest != null) dest.writeString(mCertDigest);
+        dest.writeByteArray(mEventData);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ MeasurementWebTriggerEventParamsParcel(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        Uri destinationUrl = (Uri) in.readTypedObject(Uri.CREATOR);
+        String appPackageName = in.readString();
+        ComponentName isolatedService = (ComponentName) in.readTypedObject(ComponentName.CREATOR);
+        String certDigest = (flg & 0x8) == 0 ? null : in.readString();
+        byte[] eventData = in.createByteArray();
+
+        this.mDestinationUrl = destinationUrl;
+        AnnotationValidations.validate(
+                NonNull.class, null, mDestinationUrl);
+        this.mAppPackageName = appPackageName;
+        AnnotationValidations.validate(
+                NonNull.class, null, mAppPackageName);
+        this.mIsolatedService = isolatedService;
+        AnnotationValidations.validate(
+                NonNull.class, null, mIsolatedService);
+        this.mCertDigest = certDigest;
+        this.mEventData = eventData;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<MeasurementWebTriggerEventParamsParcel> CREATOR
+            = new Parcelable.Creator<MeasurementWebTriggerEventParamsParcel>() {
+        @Override
+        public MeasurementWebTriggerEventParamsParcel[] newArray(int size) {
+            return new MeasurementWebTriggerEventParamsParcel[size];
+        }
+
+        @Override
+        public MeasurementWebTriggerEventParamsParcel createFromParcel(@NonNull android.os.Parcel in) {
+            return new MeasurementWebTriggerEventParamsParcel(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1707510209072L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/MeasurementWebTriggerEventParamsParcel.java",
+            inputSignatures = "private @android.annotation.NonNull android.net.Uri mDestinationUrl\nprivate @android.annotation.NonNull java.lang.String mAppPackageName\nprivate @android.annotation.NonNull android.content.ComponentName mIsolatedService\nprivate @android.annotation.Nullable java.lang.String mCertDigest\nprivate @android.annotation.Nullable byte[] mEventData\nclass MeasurementWebTriggerEventParamsParcel extends java.lang.Object implements [android.os.Parcelable]\[email protected](genAidl=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/ModelId.java b/android-35/android/adservices/ondevicepersonalization/ModelId.java
new file mode 100644
index 0000000..0d092a3
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/ModelId.java
@@ -0,0 +1,225 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** @hide */
+@DataClass(genAidl = false, genBuilder = true, genEqualsHashCode = true)
+public final class ModelId implements Parcelable {
+
+    public static final int TABLE_ID_REMOTE_DATA = 1;
+    public static final int TABLE_ID_LOCAL_DATA = 2;
+
+    @IntDef(
+            prefix = "TABLE_ID_",
+            value = {TABLE_ID_REMOTE_DATA, TABLE_ID_LOCAL_DATA})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TABLE {}
+
+    // The table name of the table where pre-trained model is stored. Only supports TFLite model
+    // now.
+    private @TABLE int mTableId;
+
+    // The key of the table where the corresponding value stores a pre-trained model. Only supports
+    // TFLite model now.
+    @NonNull private String mKey;
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen
+    // $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/ModelId.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    // @formatter:off
+
+    @IntDef(
+            prefix = "TABLE_ID_",
+            value = {TABLE_ID_REMOTE_DATA, TABLE_ID_LOCAL_DATA})
+    @Retention(RetentionPolicy.SOURCE)
+    @DataClass.Generated.Member
+    public @interface TableId {}
+
+    @DataClass.Generated.Member
+    public static String tableIdToString(@TableId int value) {
+        switch (value) {
+            case TABLE_ID_REMOTE_DATA:
+                return "TABLE_ID_REMOTE_DATA";
+            case TABLE_ID_LOCAL_DATA:
+                return "TABLE_ID_LOCAL_DATA";
+            default:
+                return Integer.toHexString(value);
+        }
+    }
+
+    @DataClass.Generated.Member
+    /* package-private */ ModelId(@TABLE int tableId, @NonNull String key) {
+        this.mTableId = tableId;
+        AnnotationValidations.validate(TABLE.class, null, mTableId);
+        this.mKey = key;
+        AnnotationValidations.validate(NonNull.class, null, mKey);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @TABLE int getTableId() {
+        return mTableId;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull String getKey() {
+        return mKey;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(ModelId other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        ModelId that = (ModelId) o;
+        //noinspection PointlessBooleanExpression
+        return true && mTableId == that.mTableId && java.util.Objects.equals(mKey, that.mKey);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + mTableId;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mKey);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeInt(mTableId);
+        dest.writeString(mKey);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ ModelId(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        int tableId = in.readInt();
+        String key = in.readString();
+
+        this.mTableId = tableId;
+        AnnotationValidations.validate(TABLE.class, null, mTableId);
+        this.mKey = key;
+        AnnotationValidations.validate(NonNull.class, null, mKey);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ModelId> CREATOR =
+            new Parcelable.Creator<ModelId>() {
+                @Override
+                public ModelId[] newArray(int size) {
+                    return new ModelId[size];
+                }
+
+                @Override
+                public ModelId createFromParcel(@NonNull android.os.Parcel in) {
+                    return new ModelId(in);
+                }
+            };
+
+    /** A builder for {@link ModelId} */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @TABLE int mTableId;
+        private @NonNull String mKey;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {}
+
+        @DataClass.Generated.Member
+        public @NonNull Builder setTableId(@TABLE int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mTableId = value;
+            return this;
+        }
+
+        @DataClass.Generated.Member
+        public @NonNull Builder setKey(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mKey = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull ModelId build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4; // Mark builder used
+
+            ModelId o = new ModelId(mTableId, mKey);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x4) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    // @formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/ModelManager.java b/android-35/android/adservices/ondevicepersonalization/ModelManager.java
new file mode 100644
index 0000000..29f6e01
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/ModelManager.java
@@ -0,0 +1,132 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.adservices.ondevicepersonalization.aidl.IDataAccessService;
+import android.adservices.ondevicepersonalization.aidl.IIsolatedModelService;
+import android.adservices.ondevicepersonalization.aidl.IIsolatedModelServiceCallback;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+import android.os.Bundle;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.LoggerFactory;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Handles model inference and only support TFLite model inference now. See {@link
+ * IsolatedService#getModelManager}.
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public class ModelManager {
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
+    private static final String TAG = ModelManager.class.getSimpleName();
+    @NonNull private final IDataAccessService mDataService;
+
+    @NonNull private final IIsolatedModelService mModelService;
+
+    /** @hide */
+    public ModelManager(
+            @NonNull IDataAccessService dataService, @NonNull IIsolatedModelService modelService) {
+        mDataService = dataService;
+        mModelService = modelService;
+    }
+
+    /**
+     * Run a single model inference. Only supports TFLite model inference now.
+     *
+     * @param input contains all the information needed for a run of model inference.
+     * @param executor the {@link Executor} on which to invoke the callback.
+     * @param receiver this returns a {@link InferenceOutput} which contains model inference result
+     *     or {@link Exception} on failure.
+     */
+    @WorkerThread
+    public void run(
+            @NonNull InferenceInput input,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<InferenceOutput, Exception> receiver) {
+        final long startTimeMillis = System.currentTimeMillis();
+        Objects.requireNonNull(input);
+        Bundle bundle = new Bundle();
+        bundle.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, mDataService.asBinder());
+        bundle.putParcelable(Constants.EXTRA_INFERENCE_INPUT, new InferenceInputParcel(input));
+        try {
+            mModelService.runInference(
+                    bundle,
+                    new IIsolatedModelServiceCallback.Stub() {
+                        @Override
+                        public void onSuccess(Bundle result) {
+                            executor.execute(
+                                    () -> {
+                                        int responseCode = Constants.STATUS_SUCCESS;
+                                        long endTimeMillis = System.currentTimeMillis();
+                                        try {
+                                            InferenceOutputParcel outputParcel =
+                                                    Objects.requireNonNull(
+                                                            result.getParcelable(
+                                                                    Constants.EXTRA_RESULT,
+                                                                    InferenceOutputParcel.class));
+                                            InferenceOutput output =
+                                                    new InferenceOutput(outputParcel.getData());
+                                            endTimeMillis = System.currentTimeMillis();
+                                            receiver.onResult(output);
+                                        } catch (Exception e) {
+                                            endTimeMillis = System.currentTimeMillis();
+                                            responseCode = Constants.STATUS_INTERNAL_ERROR;
+                                            receiver.onError(e);
+                                        } finally {
+                                            logApiCallStats(
+                                                    Constants.API_NAME_MODEL_MANAGER_RUN,
+                                                    endTimeMillis - startTimeMillis,
+                                                    responseCode);
+                                        }
+                                    });
+                        }
+
+                        @Override
+                        public void onError(int errorCode) {
+                            executor.execute(
+                                    () -> {
+                                        long endTimeMillis = System.currentTimeMillis();
+                                        receiver.onError(
+                                            new IllegalStateException("Error: " + errorCode));
+                                        logApiCallStats(
+                                                Constants.API_NAME_MODEL_MANAGER_RUN,
+                                                endTimeMillis - startTimeMillis,
+                                                Constants.STATUS_INTERNAL_ERROR);
+                                    });
+                        }
+                    });
+        } catch (RemoteException e) {
+            receiver.onError(new IllegalStateException(e));
+        }
+    }
+
+    private void logApiCallStats(int apiName, long duration, int responseCode) {
+        try {
+            mDataService.logApiCallStats(apiName, duration, responseCode);
+        } catch (Exception e) {
+            sLogger.d(e, TAG + ": failed to log metrics");
+        }
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/MutableKeyValueStore.java b/android-35/android/adservices/ondevicepersonalization/MutableKeyValueStore.java
new file mode 100644
index 0000000..d20fc31
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/MutableKeyValueStore.java
@@ -0,0 +1,57 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+
+/**
+ * An interface to a read-write key-value store.
+ *
+ * Used as a Data Access Object for the LOCAL_DATA table.
+ *
+ * @see IsolatedService#getLocalData(RequestToken)
+ *
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public interface MutableKeyValueStore extends KeyValueStore {
+    /**
+     * Associates the specified value with the specified key.
+     * If a value already exists for that key, the old value is replaced.
+     *
+     * @param key key with which the specified value is to be associated
+     * @param value value to be associated with the specified key
+     *
+     * @return the previous value associated with key, or null if there was no mapping for key.
+     */
+    @WorkerThread
+    @Nullable byte[] put(@NonNull String key, @NonNull byte[] value);
+
+    /**
+     * Removes the mapping for the specified key.
+     *
+     * @param key key whose mapping is to be removed
+     *
+     * @return the previous value associated with key, or null if there was no mapping for key.
+     */
+    @WorkerThread
+    @Nullable byte[] remove(@NonNull String key);
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/OSVersion.java b/android-35/android/adservices/ondevicepersonalization/OSVersion.java
new file mode 100644
index 0000000..946da82
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/OSVersion.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * Values for OS versions.
+*
+* @hide
+*/
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public final class OSVersion implements Parcelable {
+    /** Major OS version. */
+    @NonNull int mMajor;
+
+    /** Minor OS version. */
+    @NonNull int mMinor;
+
+    /** Micro OS version. */
+    @NonNull int mMicro;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/OSVersion.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ OSVersion(
+            @NonNull int major,
+            @NonNull int minor,
+            @NonNull int micro) {
+        this.mMajor = major;
+        AnnotationValidations.validate(
+                NonNull.class, null, mMajor);
+        this.mMinor = minor;
+        AnnotationValidations.validate(
+                NonNull.class, null, mMinor);
+        this.mMicro = micro;
+        AnnotationValidations.validate(
+                NonNull.class, null, mMicro);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Major OS version.
+     */
+    @DataClass.Generated.Member
+    public @NonNull int getMajor() {
+        return mMajor;
+    }
+
+    /**
+     * Minor OS version.
+     */
+    @DataClass.Generated.Member
+    public @NonNull int getMinor() {
+        return mMinor;
+    }
+
+    /**
+     * Micro OS version.
+     */
+    @DataClass.Generated.Member
+    public @NonNull int getMicro() {
+        return mMicro;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(OSVersion other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        OSVersion that = (OSVersion) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && mMajor == that.mMajor
+                && mMinor == that.mMinor
+                && mMicro == that.mMicro;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + mMajor;
+        _hash = 31 * _hash + mMinor;
+        _hash = 31 * _hash + mMicro;
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeInt(mMajor);
+        dest.writeInt(mMinor);
+        dest.writeInt(mMicro);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ OSVersion(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        int major = in.readInt();
+        int minor = in.readInt();
+        int micro = in.readInt();
+
+        this.mMajor = major;
+        AnnotationValidations.validate(
+                NonNull.class, null, mMajor);
+        this.mMinor = minor;
+        AnnotationValidations.validate(
+                NonNull.class, null, mMinor);
+        this.mMicro = micro;
+        AnnotationValidations.validate(
+                NonNull.class, null, mMicro);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<OSVersion> CREATOR
+            = new Parcelable.Creator<OSVersion>() {
+        @Override
+        public OSVersion[] newArray(int size) {
+            return new OSVersion[size];
+        }
+
+        @Override
+        public OSVersion createFromParcel(@NonNull android.os.Parcel in) {
+            return new OSVersion(in);
+        }
+    };
+
+    /**
+     * A builder for {@link OSVersion}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @NonNull int mMajor;
+        private @NonNull int mMinor;
+        private @NonNull int mMicro;
+
+        private long mBuilderFieldsSet = 0L;
+
+        /**
+         * Creates a new Builder.
+         *
+         * @param major
+         *   Major OS version.
+         * @param minor
+         *   Minor OS version.
+         * @param micro
+         *   Micro OS version.
+         */
+        public Builder(
+                @NonNull int major,
+                @NonNull int minor,
+                @NonNull int micro) {
+            mMajor = major;
+            AnnotationValidations.validate(
+                    NonNull.class, null, mMajor);
+            mMinor = minor;
+            AnnotationValidations.validate(
+                    NonNull.class, null, mMinor);
+            mMicro = micro;
+            AnnotationValidations.validate(
+                    NonNull.class, null, mMicro);
+        }
+
+        public Builder() {
+        }
+
+        /**
+         * Major OS version.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setMajor(@NonNull int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mMajor = value;
+            return this;
+        }
+
+        /**
+         * Minor OS version.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setMinor(@NonNull int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mMinor = value;
+            return this;
+        }
+
+        /**
+         * Micro OS version.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setMicro(@NonNull int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mMicro = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull OSVersion build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8; // Mark builder used
+
+            OSVersion o = new OSVersion(
+                    mMajor,
+                    mMinor,
+                    mMicro);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x8) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1692118390970L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/OSVersion.java",
+            inputSignatures = " @android.annotation.NonNull int mMajor\n @android.annotation.NonNull int mMinor\n @android.annotation.NonNull int mMicro\nclass OSVersion extends java.lang.Object implements [android.os.Parcelable]\[email protected](genBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationConfigManager.java b/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationConfigManager.java
new file mode 100644
index 0000000..df3fc3b
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationConfigManager.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import static android.adservices.ondevicepersonalization.OnDevicePersonalizationPermissions.MODIFY_ONDEVICEPERSONALIZATION_STATE;
+
+import android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationConfigService;
+import android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationConfigServiceCallback;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Binder;
+import android.os.OutcomeReceiver;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.federatedcompute.internal.util.AbstractServiceBinder;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.ondevicepersonalization.internal.util.LoggerFactory;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+
+/**
+ * OnDevicePersonalizationConfigManager provides system APIs
+ * for privileged APKs to control OnDevicePersonalization's enablement status.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public class OnDevicePersonalizationConfigManager {
+    /** @hide */
+    public static final String ON_DEVICE_PERSONALIZATION_CONFIG_SERVICE =
+            "on_device_personalization_config_service";
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
+    private static final String TAG = OnDevicePersonalizationConfigManager.class.getSimpleName();
+
+    private static final String ODP_CONFIG_SERVICE_PACKAGE_SUFFIX =
+            "com.android.ondevicepersonalization.services";
+
+    private static final String ALT_ODP_CONFIG_SERVICE_PACKAGE_SUFFIX =
+            "com.google.android.ondevicepersonalization.services";
+    private static final String ODP_CONFIG_SERVICE_INTENT =
+            "android.OnDevicePersonalizationConfigService";
+
+    private final AbstractServiceBinder<IOnDevicePersonalizationConfigService> mServiceBinder;
+
+    /** @hide */
+    public OnDevicePersonalizationConfigManager(@NonNull Context context) {
+        this(
+                AbstractServiceBinder.getServiceBinderByIntent(
+                        context,
+                        ODP_CONFIG_SERVICE_INTENT,
+                        List.of(
+                                ODP_CONFIG_SERVICE_PACKAGE_SUFFIX,
+                                ALT_ODP_CONFIG_SERVICE_PACKAGE_SUFFIX),
+                        IOnDevicePersonalizationConfigService.Stub::asInterface));
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public OnDevicePersonalizationConfigManager(
+            AbstractServiceBinder<IOnDevicePersonalizationConfigService> serviceBinder) {
+        this.mServiceBinder = serviceBinder;
+    }
+
+    /**
+     * API users are expected to call this to modify personalization status for
+     * On Device Personalization. The status is persisted both in memory and to the disk.
+     * When reboot, the in-memory status will be restored from the disk.
+     * Personalization is disabled by default.
+     *
+     * @param enabled boolean whether On Device Personalization should be enabled.
+     * @param executor The {@link Executor} on which to invoke the callback.
+     * @param receiver This either returns null on success or {@link Exception} on failure.
+     *
+     *     In case of an error, the receiver returns one of the following exceptions:
+     *     Returns an {@link IllegalStateException} if the callback is unable to send back results.
+     *     Returns a {@link SecurityException} if the caller is unauthorized to modify
+     *     personalization status.
+     */
+    @RequiresPermission(MODIFY_ONDEVICEPERSONALIZATION_STATE)
+    public void setPersonalizationEnabled(boolean enabled,
+                                          @NonNull @CallbackExecutor Executor executor,
+                                          @NonNull OutcomeReceiver<Void, Exception> receiver) {
+        CountDownLatch latch = new CountDownLatch(1);
+        try {
+            IOnDevicePersonalizationConfigService service = mServiceBinder.getService(executor);
+            service.setPersonalizationStatus(enabled,
+                    new IOnDevicePersonalizationConfigServiceCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> {
+                                    receiver.onResult(null);
+                                    latch.countDown();
+                                });
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+
+                        @Override
+                        public void onFailure(int errorCode) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> {
+                                    sLogger.w(TAG + ": Unexpected failure from ODP"
+                                            + "config service with error code: " + errorCode);
+                                    receiver.onError(
+                                            new IllegalStateException("Unexpected failure."));
+                                    latch.countDown();
+                                });
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (IllegalArgumentException | NullPointerException e) {
+            latch.countDown();
+            throw e;
+        } catch (SecurityException e) {
+            sLogger.w(TAG + ": Unauthorized call to ODP config service.");
+            receiver.onError(e);
+            latch.countDown();
+        } catch (Exception e) {
+            sLogger.w(TAG + ": Unexpected exception during call to ODP config service.");
+            receiver.onError(e);
+            latch.countDown();
+        } finally {
+            try {
+                latch.await();
+            } catch (InterruptedException e) {
+                sLogger.e(TAG + ": Failed to set personalization.", e);
+                receiver.onError(e);
+            }
+            unbindFromService();
+        }
+    }
+
+    /**
+     * Unbind from config service.
+     */
+    private void unbindFromService() {
+        mServiceBinder.unbindFromService();
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationDebugManager.java b/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationDebugManager.java
new file mode 100644
index 0000000..d055838
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationDebugManager.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationDebugService;
+import android.content.Context;
+import android.os.RemoteException;
+
+import com.android.federatedcompute.internal.util.AbstractServiceBinder;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executors;
+
+/**
+ * Provides APIs to support testing.
+ * @hide
+ */
+public class OnDevicePersonalizationDebugManager {
+
+    private static final String INTENT_FILTER_ACTION =
+            "android.OnDevicePersonalizationDebugService";
+    private static final String ODP_MANAGING_SERVICE_PACKAGE_SUFFIX =
+            "com.android.ondevicepersonalization.services";
+
+    private static final String ALT_ODP_MANAGING_SERVICE_PACKAGE_SUFFIX =
+            "com.google.android.ondevicepersonalization.services";
+
+    private final AbstractServiceBinder<IOnDevicePersonalizationDebugService> mServiceBinder;
+
+    public OnDevicePersonalizationDebugManager(Context context) {
+        mServiceBinder =
+                AbstractServiceBinder.getServiceBinderByIntent(
+                        context,
+                        INTENT_FILTER_ACTION,
+                        List.of(
+                                ODP_MANAGING_SERVICE_PACKAGE_SUFFIX,
+                                ALT_ODP_MANAGING_SERVICE_PACKAGE_SUFFIX),
+                        IOnDevicePersonalizationDebugService.Stub::asInterface);
+    }
+
+    /** Returns whether the service is enabled. */
+    public Boolean isEnabled() {
+        try {
+            IOnDevicePersonalizationDebugService service = Objects.requireNonNull(
+                    mServiceBinder.getService(Executors.newSingleThreadExecutor()));
+            boolean result = service.isEnabled();
+            mServiceBinder.unbindFromService();
+            return result;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationException.java b/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationException.java
new file mode 100644
index 0000000..47b0128
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationException.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Exception thrown by OnDevicePersonalization APIs.
+ *
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public class OnDevicePersonalizationException extends Exception {
+    /**
+     * The {@link IsolatedService} that was invoked failed to run.
+     */
+    public static final int ERROR_ISOLATED_SERVICE_FAILED = 1;
+
+    /**
+     * The {@link IsolatedService} was not started because personalization is disabled by
+     * device configuration.
+     */
+    public static final int ERROR_PERSONALIZATION_DISABLED = 2;
+
+    /** @hide */
+    @IntDef(prefix = "ERROR_", value = {
+            ERROR_ISOLATED_SERVICE_FAILED,
+            ERROR_PERSONALIZATION_DISABLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ErrorCode {}
+
+    private final @ErrorCode int mErrorCode;
+
+    /** @hide */
+    public OnDevicePersonalizationException(@ErrorCode int errorCode) {
+        mErrorCode = errorCode;
+    }
+
+    /** @hide */
+    public OnDevicePersonalizationException(
+            @ErrorCode int errorCode, String message) {
+        super(message);
+        mErrorCode = errorCode;
+    }
+
+    /** @hide */
+    public OnDevicePersonalizationException(
+            @ErrorCode int errorCode, Throwable cause) {
+        super(cause);
+        mErrorCode = errorCode;
+    }
+
+    /** Returns the error code for this exception. */
+    public @ErrorCode int getErrorCode() {
+        return mErrorCode;
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java b/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java
new file mode 100644
index 0000000..5b00b2a
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.adservices.ondevicepersonalization.aidl.IExecuteCallback;
+import android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationManagingService;
+import android.adservices.ondevicepersonalization.aidl.IRequestSurfacePackageCallback;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.OutcomeReceiver;
+import android.os.PersistableBundle;
+import android.os.SystemClock;
+import android.view.SurfaceControlViewHost;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.federatedcompute.internal.util.AbstractServiceBinder;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
+import com.android.ondevicepersonalization.internal.util.ByteArrayParceledSlice;
+import com.android.ondevicepersonalization.internal.util.LoggerFactory;
+import com.android.ondevicepersonalization.internal.util.PersistableBundleUtils;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+// TODO(b/289102463): Add a link to the public ODP developer documentation.
+/**
+ * OnDevicePersonalizationManager provides APIs for apps to load an
+ * {@link IsolatedService} in an isolated process and interact with it.
+ *
+ * An app can request an {@link IsolatedService} to generate content for display
+ * within an {@link android.view.SurfaceView} within the app's view hierarchy, and also write
+ * persistent results to on-device storage which can be consumed by Federated Analytics for
+ * cross-device statistical analysis or by Federated Learning for model training. The displayed
+ * content and the persistent output are both not directly accessible by the calling app.
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public class OnDevicePersonalizationManager {
+    /** @hide */
+    public static final String ON_DEVICE_PERSONALIZATION_SERVICE =
+            "on_device_personalization_service";
+    private static final String INTENT_FILTER_ACTION = "android.OnDevicePersonalizationService";
+    private static final String ODP_MANAGING_SERVICE_PACKAGE_SUFFIX =
+            "com.android.ondevicepersonalization.services";
+
+    private static final String ALT_ODP_MANAGING_SERVICE_PACKAGE_SUFFIX =
+            "com.google.android.ondevicepersonalization.services";
+    private static final String TAG = OnDevicePersonalizationManager.class.getSimpleName();
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
+    private final AbstractServiceBinder<IOnDevicePersonalizationManagingService> mServiceBinder;
+    private final Context mContext;
+
+    /**
+     * The result of a call to {@link OnDevicePersonalizationManager#execute(ComponentName,
+     * PersistableBundle, Executor, OutcomeReceiver)}
+     */
+    public static class ExecuteResult {
+        @Nullable private final SurfacePackageToken mSurfacePackageToken;
+        @Nullable private final byte[] mOutputData;
+
+        /** @hide */
+        ExecuteResult(
+                @Nullable SurfacePackageToken surfacePackageToken,
+                @Nullable byte[] outputData) {
+            mSurfacePackageToken = surfacePackageToken;
+            mOutputData = outputData;
+        }
+
+        /**
+         * Returns a {@link SurfacePackageToken}, which is an opaque reference to content that
+         * can be displayed in a {@link android.view.SurfaceView}. This may be null if the
+         * {@link IsolatedService} has not generated any content to be displayed within the
+         * calling app.
+         */
+        @Nullable public SurfacePackageToken getSurfacePackageToken() {
+            return mSurfacePackageToken;
+        }
+
+        /**
+         * Returns the output data that was returned by the {@link IsolatedService}. This will be
+         * non-null if the {@link IsolatedService} returns any results to the caller, and the
+         * egress of data from the {@link IsolatedService} to the specific calling app is allowed
+         * by policy as well as an allowlist.
+         */
+        @Nullable public byte[] getOutputData() {
+            return mOutputData;
+        }
+    }
+
+    /** @hide */
+    public OnDevicePersonalizationManager(Context context) {
+        this(
+                context,
+                AbstractServiceBinder.getServiceBinderByIntent(
+                        context,
+                        INTENT_FILTER_ACTION,
+                        List.of(
+                                ODP_MANAGING_SERVICE_PACKAGE_SUFFIX,
+                                ALT_ODP_MANAGING_SERVICE_PACKAGE_SUFFIX),
+                        SdkLevel.isAtLeastU() ? Context.BIND_ALLOW_ACTIVITY_STARTS : 0,
+                        IOnDevicePersonalizationManagingService.Stub::asInterface));
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public OnDevicePersonalizationManager(
+            Context context,
+            AbstractServiceBinder<IOnDevicePersonalizationManagingService> serviceBinder) {
+        mContext = context;
+        mServiceBinder = serviceBinder;
+    }
+
+    /**
+     * Executes an {@link IsolatedService} in the OnDevicePersonalization sandbox. The
+     * platform binds to the specified {@link IsolatedService} in an isolated process
+     * and calls {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}
+     * with the caller-provided parameters. When the {@link IsolatedService} finishes execution,
+     * the platform returns tokens that refer to the results from the service to the caller.
+     * These tokens can be subsequently used to display results in a
+     * {@link android.view.SurfaceView} within the calling app.
+     *
+     * @param service The {@link ComponentName} of the {@link IsolatedService}.
+     * @param params a {@link PersistableBundle} that is passed from the calling app to the
+     *     {@link IsolatedService}. The expected contents of this parameter are defined
+     *     by the{@link IsolatedService}. The platform does not interpret this parameter.
+     * @param executor the {@link Executor} on which to invoke the callback.
+     * @param receiver This returns a {@link ExecuteResult} object on success or an
+     *     {@link Exception} on failure. If the
+     *     {@link IsolatedService} returned a {@link RenderingConfig} to be displayed,
+     *     {@link ExecuteResult#getSurfacePackageToken()} will return a non-null
+     *     {@link SurfacePackageToken}.
+     *     The {@link SurfacePackageToken} object can be used in a subsequent
+     *     {@link #requestSurfacePackage(SurfacePackageToken, IBinder, int, int, int, Executor,
+     *     OutcomeReceiver)} call to display the result in a view. The returned
+     *     {@link SurfacePackageToken} may be null to indicate that no output is expected to be
+     *     displayed for this request. If the {@link IsolatedService} has returned any output data
+     *     and the calling app is allowlisted to receive data from this service, the
+     *     {@link ExecuteResult#getOutputData()} will return a non-null byte array.
+     *
+     *     In case of an error, the receiver returns one of the following exceptions:
+     *     Returns a {@link android.content.pm.PackageManager.NameNotFoundException} if the handler
+     *     package is not installed or does not have a valid ODP manifest.
+     *     Returns {@link ClassNotFoundException} if the handler class is not found.
+     *     Returns an {@link OnDevicePersonalizationException} if execution of the handler fails.
+     */
+    public void execute(
+            @NonNull ComponentName service,
+            @NonNull PersistableBundle params,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<ExecuteResult, Exception> receiver
+    ) {
+        Objects.requireNonNull(service);
+        Objects.requireNonNull(params);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+        Objects.requireNonNull(service.getPackageName());
+        Objects.requireNonNull(service.getClassName());
+        if (service.getPackageName().isEmpty()) {
+            throw new IllegalArgumentException("missing service package name");
+        }
+        if (service.getClassName().isEmpty()) {
+            throw new IllegalArgumentException("missing service class name");
+        }
+        long startTimeMillis = SystemClock.elapsedRealtime();
+
+        try {
+            final IOnDevicePersonalizationManagingService odpService =
+                    mServiceBinder.getService(executor);
+
+            try {
+                IExecuteCallback callbackWrapper = new IExecuteCallback.Stub() {
+                    @Override
+                    public void onSuccess(
+                            Bundle callbackResult) {
+                        final long token = Binder.clearCallingIdentity();
+                        try {
+                            executor.execute(() -> {
+                                try {
+                                    SurfacePackageToken surfacePackageToken = null;
+                                    if (callbackResult != null) {
+                                        String tokenString = callbackResult.getString(
+                                                Constants.EXTRA_SURFACE_PACKAGE_TOKEN_STRING);
+                                        if (tokenString != null && !tokenString.isBlank()) {
+                                            surfacePackageToken = new SurfacePackageToken(
+                                                    tokenString);
+                                        }
+                                    }
+                                    byte[] data = callbackResult.getByteArray(
+                                            Constants.EXTRA_OUTPUT_DATA);
+                                    receiver.onResult(
+                                            new ExecuteResult(surfacePackageToken, data));
+                                } catch (Exception e) {
+                                    receiver.onError(e);
+                                }
+                            });
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                            logApiCallStats(
+                                    odpService,
+                                    Constants.API_NAME_EXECUTE,
+                                    SystemClock.elapsedRealtime() - startTimeMillis,
+                                    Constants.STATUS_SUCCESS);
+                        }
+                    }
+
+                    @Override
+                    public void onError(
+                            int errorCode, int isolatedServiceErrorCode, String message) {
+                        final long token = Binder.clearCallingIdentity();
+                        try {
+                            executor.execute(() -> receiver.onError(
+                                    createException(
+                                            errorCode, isolatedServiceErrorCode, message)));
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                            logApiCallStats(
+                                    odpService,
+                                    Constants.API_NAME_EXECUTE,
+                                    SystemClock.elapsedRealtime() - startTimeMillis,
+                                    errorCode);
+                        }
+
+                    }
+                };
+
+                Bundle wrappedParams = new Bundle();
+                wrappedParams.putParcelable(
+                        Constants.EXTRA_APP_PARAMS_SERIALIZED,
+                        new ByteArrayParceledSlice(PersistableBundleUtils.toByteArray(params)));
+                odpService.execute(
+                        mContext.getPackageName(),
+                        service,
+                        wrappedParams,
+                        new CallerMetadata.Builder().setStartTimeMillis(startTimeMillis).build(),
+                        callbackWrapper);
+
+            } catch (Exception e) {
+                logApiCallStats(
+                        odpService,
+                        Constants.API_NAME_EXECUTE,
+                        SystemClock.elapsedRealtime() - startTimeMillis,
+                        Constants.STATUS_INTERNAL_ERROR);
+                receiver.onError(e);
+            }
+
+        } catch (Exception e) {
+            receiver.onError(e);
+        }
+    }
+
+    /**
+     * Requests a {@link android.view.SurfaceControlViewHost.SurfacePackage} to be inserted into a
+     * {@link android.view.SurfaceView} inside the calling app. The surface package will contain an
+     * {@link android.view.View} with the content from a result of a prior call to
+     * {@code #execute(ComponentName, PersistableBundle, Executor, OutcomeReceiver)} running in
+     * the OnDevicePersonalization sandbox.
+     *
+     * @param surfacePackageToken a reference to a {@link SurfacePackageToken} returned by a prior
+     *     call to {@code #execute(ComponentName, PersistableBundle, Executor, OutcomeReceiver)}.
+     * @param surfaceViewHostToken the hostToken of the {@link android.view.SurfaceView}, which is
+     *     returned by {@link android.view.SurfaceView#getHostToken()} after the
+     *     {@link android.view.SurfaceView} has been added to the view hierarchy.
+     * @param displayId the integer ID of the logical display on which to display the
+     *     {@link android.view.SurfaceControlViewHost.SurfacePackage}, returned by
+     *     {@code Context.getDisplay().getDisplayId()}.
+     * @param width the width of the {@link android.view.SurfaceControlViewHost.SurfacePackage}
+     *     in pixels.
+     * @param height the height of the {@link android.view.SurfaceControlViewHost.SurfacePackage}
+     *     in pixels.
+     * @param executor the {@link Executor} on which to invoke the callback
+     * @param receiver This either returns a
+     *     {@link android.view.SurfaceControlViewHost.SurfacePackage} on success, or
+     *     {@link Exception} on failure. The exception type is
+     *     {@link OnDevicePersonalizationException} if execution of the handler fails.
+     */
+    public void requestSurfacePackage(
+            @NonNull SurfacePackageToken surfacePackageToken,
+            @NonNull IBinder surfaceViewHostToken,
+            int displayId,
+            int width,
+            int height,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<SurfaceControlViewHost.SurfacePackage, Exception> receiver
+    ) {
+        Objects.requireNonNull(surfacePackageToken);
+        Objects.requireNonNull(surfaceViewHostToken);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+        if (width <= 0) {
+            throw new IllegalArgumentException("width must be > 0");
+        }
+
+        if (height <= 0) {
+            throw new IllegalArgumentException("height must be > 0");
+        }
+
+        if (displayId < 0) {
+            throw new IllegalArgumentException("displayId must be >= 0");
+        }
+        long startTimeMillis = SystemClock.elapsedRealtime();
+
+        try {
+            final IOnDevicePersonalizationManagingService service =
+                    Objects.requireNonNull(mServiceBinder.getService(executor));
+
+            try {
+                IRequestSurfacePackageCallback callbackWrapper =
+                        new IRequestSurfacePackageCallback.Stub() {
+                            @Override
+                            public void onSuccess(
+                                    SurfaceControlViewHost.SurfacePackage surfacePackage) {
+                                final long token = Binder.clearCallingIdentity();
+                                try {
+                                    executor.execute(() -> {
+                                        receiver.onResult(surfacePackage);
+                                    });
+                                } finally {
+                                    Binder.restoreCallingIdentity(token);
+                                    logApiCallStats(
+                                            service,
+                                            Constants.API_NAME_REQUEST_SURFACE_PACKAGE,
+                                            SystemClock.elapsedRealtime() - startTimeMillis,
+                                            Constants.STATUS_SUCCESS);
+                                }
+                            }
+
+                            @Override
+                            public void onError(
+                                    int errorCode, int isolatedServiceErrorCode, String message) {
+                                final long token = Binder.clearCallingIdentity();
+                                try {
+                                    executor.execute(
+                                            () -> receiver.onError(createException(
+                                                    errorCode, isolatedServiceErrorCode,
+                                                    message)));
+                                } finally {
+                                    Binder.restoreCallingIdentity(token);
+                                    logApiCallStats(
+                                            service,
+                                            Constants.API_NAME_REQUEST_SURFACE_PACKAGE,
+                                            SystemClock.elapsedRealtime() - startTimeMillis,
+                                            errorCode);
+                                }
+                            }
+                        };
+
+                service.requestSurfacePackage(
+                        surfacePackageToken.getTokenString(),
+                        surfaceViewHostToken,
+                        displayId,
+                        width,
+                        height,
+                        new CallerMetadata.Builder().setStartTimeMillis(startTimeMillis).build(),
+                        callbackWrapper);
+
+            } catch (Exception e) {
+                logApiCallStats(
+                        service,
+                        Constants.API_NAME_REQUEST_SURFACE_PACKAGE,
+                        SystemClock.elapsedRealtime() - startTimeMillis,
+                        Constants.STATUS_INTERNAL_ERROR);
+                receiver.onError(e);
+            }
+
+        } catch (Exception e) {
+            receiver.onError(e);
+        }
+    }
+
+    private Exception createException(
+            int errorCode, int isolatedServiceErrorCode, String message) {
+        if (message == null || message.isBlank()) {
+            message = "Error: " + errorCode;
+        }
+        if (errorCode == Constants.STATUS_NAME_NOT_FOUND) {
+            return new PackageManager.NameNotFoundException();
+        } else if (errorCode == Constants.STATUS_CLASS_NOT_FOUND) {
+            return new ClassNotFoundException();
+        } else if (errorCode == Constants.STATUS_SERVICE_FAILED) {
+            if (isolatedServiceErrorCode > 0 && isolatedServiceErrorCode < 128) {
+                return new OnDevicePersonalizationException(
+                        OnDevicePersonalizationException.ERROR_ISOLATED_SERVICE_FAILED,
+                        new IsolatedServiceException(isolatedServiceErrorCode));
+            } else {
+            return new OnDevicePersonalizationException(
+                    OnDevicePersonalizationException.ERROR_ISOLATED_SERVICE_FAILED,
+                    message);
+            }
+        } else if (errorCode == Constants.STATUS_PERSONALIZATION_DISABLED) {
+            return new OnDevicePersonalizationException(
+                    OnDevicePersonalizationException.ERROR_PERSONALIZATION_DISABLED,
+                    message);
+        } else {
+            return new IllegalStateException(message);
+        }
+    }
+
+    private void logApiCallStats(
+            IOnDevicePersonalizationManagingService service,
+            int apiName,
+            long latencyMillis,
+            int responseCode) {
+        try {
+            if (service != null) {
+                service.logApiCallStats(apiName, latencyMillis, responseCode);
+            }
+        } catch (Exception e) {
+            sLogger.e(e, TAG + ": Error logging API call stats");
+        }
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationPermissions.java b/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationPermissions.java
new file mode 100644
index 0000000..ecf3b9c
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationPermissions.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+
+/**
+ * OnDevicePersonalization permission settings.
+ *
+ * @hide
+*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public class OnDevicePersonalizationPermissions {
+    private OnDevicePersonalizationPermissions() {}
+
+    /**
+     * The permission that lets it modify ODP's enablement state.
+     */
+    public static final String MODIFY_ONDEVICEPERSONALIZATION_STATE =
+            "android.permission.ondevicepersonalization.MODIFY_ONDEVICEPERSONALIZATION_STATE";
+
+    /**
+     * The permission required for callers to send measurement events to ODP.
+     */
+    public static final String NOTIFY_MEASUREMENT_EVENT =
+            "android.permission.ondevicepersonalization.NOTIFY_MEASUREMENT_EVENT";
+
+    /**
+     * The permission required to connect to the ODP system server component.
+     * @hide
+     */
+    public static final String ACCESS_SYSTEM_SERVER_SERVICE =
+            "android.permission.ondevicepersonalization.ACCESS_SYSTEM_SERVER_SERVICE";
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationSystemEventManager.java b/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationSystemEventManager.java
new file mode 100644
index 0000000..a09397e
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/OnDevicePersonalizationSystemEventManager.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import static android.adservices.ondevicepersonalization.OnDevicePersonalizationPermissions.NOTIFY_MEASUREMENT_EVENT;
+
+import android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationManagingService;
+import android.adservices.ondevicepersonalization.aidl.IRegisterMeasurementEventCallback;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.OutcomeReceiver;
+import android.os.SystemClock;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.federatedcompute.internal.util.AbstractServiceBinder;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.ondevicepersonalization.internal.util.LoggerFactory;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Provides APIs for the platform to signal events that are to be handled by the ODP service.
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public class OnDevicePersonalizationSystemEventManager {
+    /** @hide */
+    public static final String ON_DEVICE_PERSONALIZATION_SYSTEM_EVENT_SERVICE =
+            "on_device_personalization_system_event_service";
+    private static final String INTENT_FILTER_ACTION =
+            "android.OnDevicePersonalizationService";
+    private static final String ODP_MANAGING_SERVICE_PACKAGE_SUFFIX =
+            "com.android.ondevicepersonalization.services";
+    private static final String ALT_ODP_MANAGING_SERVICE_PACKAGE_SUFFIX =
+            "com.google.android.ondevicepersonalization.services";
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
+
+    // TODO(b/301732670): Define a new service for this manager and bind to it.
+    private final AbstractServiceBinder<IOnDevicePersonalizationManagingService> mServiceBinder;
+    private final Context mContext;
+
+    /** @hide */
+    public OnDevicePersonalizationSystemEventManager(Context context) {
+        this(
+                context,
+                AbstractServiceBinder.getServiceBinderByIntent(
+                        context,
+                        INTENT_FILTER_ACTION,
+                        List.of(
+                                ODP_MANAGING_SERVICE_PACKAGE_SUFFIX,
+                                ALT_ODP_MANAGING_SERVICE_PACKAGE_SUFFIX),
+                        0,
+                        IOnDevicePersonalizationManagingService.Stub::asInterface));
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public OnDevicePersonalizationSystemEventManager(
+            Context context,
+            AbstractServiceBinder<IOnDevicePersonalizationManagingService> serviceBinder) {
+        mContext = context;
+        mServiceBinder = serviceBinder;
+    }
+
+    /**
+     * Receives a web trigger event from the Measurement API. This is intended to be called by the
+     * <a href="https://developer.android.com/design-for-safety/privacy-sandbox/guides/attribution">
+     * Measurement Service</a> when a browser registers an attribution event using the
+     * <a href="https://github.com/WICG/attribution-reporting-api">Attribution and Reporting API</a>
+     * with a payload that should be processed by an {@link IsolatedService}.
+     *
+     * @param measurementWebTriggerEvent the web trigger payload to be processed.
+     * @param executor the {@link Executor} on which to invoke the callback.
+     * @param receiver This either returns a {@code null} on success, or an exception on failure.
+     */
+    @RequiresPermission(NOTIFY_MEASUREMENT_EVENT)
+    public void notifyMeasurementEvent(
+            @NonNull MeasurementWebTriggerEventParams measurementWebTriggerEvent,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Void, Exception> receiver) {
+        Objects.requireNonNull(measurementWebTriggerEvent);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+        long startTimeMillis = SystemClock.elapsedRealtime();
+
+        try {
+            final IOnDevicePersonalizationManagingService service =
+                    mServiceBinder.getService(executor);
+            Bundle bundle = new Bundle();
+            bundle.putParcelable(Constants.EXTRA_MEASUREMENT_WEB_TRIGGER_PARAMS,
+                    new MeasurementWebTriggerEventParamsParcel(measurementWebTriggerEvent));
+            // TODO(b/301732670): Update method name in service.
+            service.registerMeasurementEvent(
+                    Constants.MEASUREMENT_EVENT_TYPE_WEB_TRIGGER,
+                    bundle,
+                    new CallerMetadata.Builder().setStartTimeMillis(startTimeMillis).build(),
+                    new IRegisterMeasurementEventCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> receiver.onResult(null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                        @Override
+                        public void onError(int errorCode) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> receiver.onError(
+                                        new IllegalStateException("Error: " + errorCode)));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    }
+            );
+        } catch (IllegalArgumentException | NullPointerException e) {
+            throw e;
+        } catch (Exception e) {
+            receiver.onError(e);
+        }
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/RemoteDataImpl.java b/android-35/android/adservices/ondevicepersonalization/RemoteDataImpl.java
new file mode 100644
index 0000000..aaa32ef
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/RemoteDataImpl.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.adservices.ondevicepersonalization.aidl.IDataAccessService;
+import android.adservices.ondevicepersonalization.aidl.IDataAccessServiceCallback;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import com.android.ondevicepersonalization.internal.util.ByteArrayParceledSlice;
+import com.android.ondevicepersonalization.internal.util.LoggerFactory;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+/** @hide */
+public class RemoteDataImpl implements KeyValueStore {
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
+    private static final String TAG = "RemoteDataImpl";
+    @NonNull
+    IDataAccessService mDataAccessService;
+
+    /** @hide */
+    public RemoteDataImpl(@NonNull IDataAccessService binder) {
+        mDataAccessService = Objects.requireNonNull(binder);
+    }
+
+    @Override @Nullable
+    public byte[] get(@NonNull String key) {
+        Objects.requireNonNull(key);
+        final long startTimeMillis = System.currentTimeMillis();
+        int responseCode = Constants.STATUS_SUCCESS;
+        try {
+            BlockingQueue<Bundle> asyncResult = new ArrayBlockingQueue<>(1);
+            Bundle params = new Bundle();
+            params.putString(Constants.EXTRA_LOOKUP_KEYS, key);
+            mDataAccessService.onRequest(
+                    Constants.DATA_ACCESS_OP_REMOTE_DATA_LOOKUP,
+                    params,
+                    new IDataAccessServiceCallback.Stub() {
+                        @Override
+                        public void onSuccess(@NonNull Bundle result) {
+                            if (result != null) {
+                                asyncResult.add(result);
+                            } else {
+                                asyncResult.add(Bundle.EMPTY);
+                            }
+                        }
+
+                        @Override
+                        public void onError(int errorCode) {
+                            asyncResult.add(Bundle.EMPTY);
+                        }
+                    });
+            Bundle result = asyncResult.take();
+            ByteArrayParceledSlice data = result.getParcelable(
+                            Constants.EXTRA_RESULT, ByteArrayParceledSlice.class);
+            return (data == null) ? null : data.getByteArray();
+        } catch (InterruptedException | RemoteException e) {
+            sLogger.e(TAG + ": Failed to retrieve key from remoteData", e);
+            responseCode = Constants.STATUS_INTERNAL_ERROR;
+            throw new IllegalStateException(e);
+        } finally {
+            try {
+                mDataAccessService.logApiCallStats(
+                        Constants.API_NAME_REMOTE_DATA_GET,
+                        System.currentTimeMillis() - startTimeMillis,
+                        responseCode);
+            } catch (Exception e) {
+                sLogger.d(e, TAG + ": failed to log metrics");
+            }
+        }
+    }
+
+    @Override @NonNull
+    public Set<String> keySet() {
+        final long startTimeMillis = System.currentTimeMillis();
+        int responseCode = Constants.STATUS_SUCCESS;
+        try {
+            BlockingQueue<Bundle> asyncResult = new ArrayBlockingQueue<>(1);
+            mDataAccessService.onRequest(
+                    Constants.DATA_ACCESS_OP_REMOTE_DATA_KEYSET,
+                    Bundle.EMPTY,
+                    new IDataAccessServiceCallback.Stub() {
+                        @Override
+                        public void onSuccess(@NonNull Bundle result) {
+                            if (result != null) {
+                                asyncResult.add(result);
+                            } else {
+                                asyncResult.add(Bundle.EMPTY);
+                            }
+                        }
+
+                        @Override
+                        public void onError(int errorCode) {
+                            asyncResult.add(Bundle.EMPTY);
+                        }
+                    });
+            Bundle result = asyncResult.take();
+            HashSet<String> resultSet =
+                    result.getSerializable(Constants.EXTRA_RESULT, HashSet.class);
+            if (null == resultSet) {
+                return Collections.emptySet();
+            }
+            return resultSet;
+        } catch (InterruptedException | RemoteException e) {
+            sLogger.e(TAG + ": Failed to retrieve keySet from remoteData", e);
+            throw new IllegalStateException(e);
+        } finally {
+            try {
+                mDataAccessService.logApiCallStats(
+                        Constants.API_NAME_REMOTE_DATA_KEYSET,
+                        System.currentTimeMillis() - startTimeMillis,
+                        responseCode);
+            } catch (Exception e) {
+                sLogger.d(e, TAG + ": failed to log metrics");
+            }
+        }
+    }
+
+    @Override
+    public int getTableId() {
+        return ModelId.TABLE_ID_REMOTE_DATA;
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/RenderInput.java b/android-35/android/adservices/ondevicepersonalization/RenderInput.java
new file mode 100644
index 0000000..9549c73
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/RenderInput.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * The input data for
+ * {@link IsolatedWorker#onRender(RenderInput, android.os.OutcomeReceiver)}.
+ *
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = false, genHiddenConstructor = true, genEqualsHashCode = true)
+public final class RenderInput {
+    /** The width of the slot. */
+    private int mWidth = 0;
+
+    /** The height of the slot. */
+    private int mHeight = 0;
+
+    /**
+     * A {@link RenderingConfig} within an {@link ExecuteOutput} that was returned by
+     * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}.
+     */
+    @Nullable RenderingConfig mRenderingConfig = null;
+
+    /** @hide */
+    public RenderInput(@NonNull RenderInputParcel parcel) {
+        this(parcel.getWidth(), parcel.getHeight(), parcel.getRenderingConfig());
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/RenderInput.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new RenderInput.
+     *
+     * @param width
+     *   The width of the slot.
+     * @param height
+     *   The height of the slot.
+     * @param renderingConfig
+     *   A {@link RenderingConfig} within an {@link ExecuteOutput} that was returned by
+     *   {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}.
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public RenderInput(
+            int width,
+            int height,
+            @Nullable RenderingConfig renderingConfig) {
+        this.mWidth = width;
+        this.mHeight = height;
+        this.mRenderingConfig = renderingConfig;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The width of the slot.
+     */
+    @DataClass.Generated.Member
+    public int getWidth() {
+        return mWidth;
+    }
+
+    /**
+     * The height of the slot.
+     */
+    @DataClass.Generated.Member
+    public int getHeight() {
+        return mHeight;
+    }
+
+    /**
+     * A {@link RenderingConfig} within an {@link ExecuteOutput} that was returned by
+     * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}.
+     */
+    @DataClass.Generated.Member
+    public @Nullable RenderingConfig getRenderingConfig() {
+        return mRenderingConfig;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(RenderInput other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        RenderInput that = (RenderInput) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && mWidth == that.mWidth
+                && mHeight == that.mHeight
+                && java.util.Objects.equals(mRenderingConfig, that.mRenderingConfig);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + mWidth;
+        _hash = 31 * _hash + mHeight;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mRenderingConfig);
+        return _hash;
+    }
+
+    @DataClass.Generated(
+            time = 1704831946167L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/RenderInput.java",
+            inputSignatures = "private  int mWidth\nprivate  int mHeight\n @android.annotation.Nullable android.adservices.ondevicepersonalization.RenderingConfig mRenderingConfig\nclass RenderInput extends java.lang.Object implements []\[email protected](genBuilder=false, genHiddenConstructor=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/RenderInputParcel.java b/android-35/android/adservices/ondevicepersonalization/RenderInputParcel.java
new file mode 100644
index 0000000..ad069bb
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/RenderInputParcel.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.Nullable;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * Parcelable version of {@link RenderInput}.
+ * @hide
+ */
+@DataClass(genAidl = false, genHiddenBuilder = true)
+public final class RenderInputParcel implements Parcelable {
+    /** The width of the slot. */
+    private int mWidth = 0;
+
+    /** The height of the slot. */
+    private int mHeight = 0;
+
+    /**
+     * A {@link RenderingConfig} within an {@link ExecuteOutput} that was returned by
+     * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}.
+     */
+    @Nullable RenderingConfig mRenderingConfig = null;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/RenderInputParcel.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ RenderInputParcel(
+            int width,
+            int height,
+            @Nullable RenderingConfig renderingConfig) {
+        this.mWidth = width;
+        this.mHeight = height;
+        this.mRenderingConfig = renderingConfig;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The width of the slot.
+     */
+    @DataClass.Generated.Member
+    public int getWidth() {
+        return mWidth;
+    }
+
+    /**
+     * The height of the slot.
+     */
+    @DataClass.Generated.Member
+    public int getHeight() {
+        return mHeight;
+    }
+
+    /**
+     * A {@link RenderingConfig} within an {@link ExecuteOutput} that was returned by
+     * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}.
+     */
+    @DataClass.Generated.Member
+    public @Nullable RenderingConfig getRenderingConfig() {
+        return mRenderingConfig;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mRenderingConfig != null) flg |= 0x4;
+        dest.writeByte(flg);
+        dest.writeInt(mWidth);
+        dest.writeInt(mHeight);
+        if (mRenderingConfig != null) dest.writeTypedObject(mRenderingConfig, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ RenderInputParcel(@android.annotation.NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        int width = in.readInt();
+        int height = in.readInt();
+        RenderingConfig renderingConfig = (flg & 0x4) == 0 ? null : (RenderingConfig) in.readTypedObject(RenderingConfig.CREATOR);
+
+        this.mWidth = width;
+        this.mHeight = height;
+        this.mRenderingConfig = renderingConfig;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @android.annotation.NonNull Parcelable.Creator<RenderInputParcel> CREATOR
+            = new Parcelable.Creator<RenderInputParcel>() {
+        @Override
+        public RenderInputParcel[] newArray(int size) {
+            return new RenderInputParcel[size];
+        }
+
+        @Override
+        public RenderInputParcel createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
+            return new RenderInputParcel(in);
+        }
+    };
+
+    /**
+     * A builder for {@link RenderInputParcel}
+     * @hide
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private int mWidth;
+        private int mHeight;
+        private @Nullable RenderingConfig mRenderingConfig;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * The width of the slot.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setWidth(int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mWidth = value;
+            return this;
+        }
+
+        /**
+         * The height of the slot.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setHeight(int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mHeight = value;
+            return this;
+        }
+
+        /**
+         * A {@link RenderingConfig} within an {@link ExecuteOutput} that was returned by
+         * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setRenderingConfig(@android.annotation.NonNull RenderingConfig value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mRenderingConfig = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @android.annotation.NonNull RenderInputParcel build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mWidth = 0;
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mHeight = 0;
+            }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mRenderingConfig = null;
+            }
+            RenderInputParcel o = new RenderInputParcel(
+                    mWidth,
+                    mHeight,
+                    mRenderingConfig);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x8) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1704831939599L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/RenderInputParcel.java",
+            inputSignatures = "private  int mWidth\nprivate  int mHeight\n @android.annotation.Nullable android.adservices.ondevicepersonalization.RenderingConfig mRenderingConfig\nclass RenderInputParcel extends java.lang.Object implements [android.os.Parcelable]\[email protected](genAidl=false, genHiddenBuilder=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/RenderOutput.java b/android-35/android/adservices/ondevicepersonalization/RenderOutput.java
new file mode 100644
index 0000000..213084a
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/RenderOutput.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.PersistableBundle;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * The result returned by
+ * {@link IsolatedWorker#onRender(RenderInput, android.os.OutcomeReceiver)}.
+ *
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public final class RenderOutput {
+    /**
+     * The HTML content to be rendered in a webview. If this is null, the ODP service
+     * generates HTML from the data in {@link #getTemplateId()} and {@link #getTemplateParams()}
+     * as described below.
+     */
+    @DataClass.MaySetToNull
+    @Nullable private String mContent = null;
+
+    /**
+     * A key in the REMOTE_DATA {@link IsolatedService#getRemoteData(RequestToken)} table that
+     * points to an <a href="velocity.apache.org">Apache Velocity</a> template. This is ignored if
+     * {@link #getContent()} is not null.
+     */
+    @DataClass.MaySetToNull
+    @Nullable private String mTemplateId = null;
+
+    /**
+     * The parameters to be populated in the template from {@link #getTemplateId()}. This is
+     * ignored if {@link #getContent()} is not null.
+     */
+    @NonNull private PersistableBundle mTemplateParams = PersistableBundle.EMPTY;
+
+
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/RenderOutput.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ RenderOutput(
+            @Nullable String content,
+            @Nullable String templateId,
+            @NonNull PersistableBundle templateParams) {
+        this.mContent = content;
+        this.mTemplateId = templateId;
+        this.mTemplateParams = templateParams;
+        AnnotationValidations.validate(
+                NonNull.class, null, mTemplateParams);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The HTML content to be rendered in a webview. If this is null, the ODP service
+     * generates HTML from the data in {@link #getTemplateId()} and {@link #getTemplateParams()}
+     * as described below.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getContent() {
+        return mContent;
+    }
+
+    /**
+     * A key in the REMOTE_DATA {@link IsolatedService#getRemoteData(RequestToken)} table that
+     * points to an <a href="velocity.apache.org">Apache Velocity</a> template. This is ignored if
+     * {@link #getContent()} is not null.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getTemplateId() {
+        return mTemplateId;
+    }
+
+    /**
+     * The parameters to be populated in the template from {@link #getTemplateId()}. This is
+     * ignored if {@link #getContent()} is not null.
+     */
+    @DataClass.Generated.Member
+    public @NonNull PersistableBundle getTemplateParams() {
+        return mTemplateParams;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(RenderOutput other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        RenderOutput that = (RenderOutput) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mContent, that.mContent)
+                && java.util.Objects.equals(mTemplateId, that.mTemplateId)
+                && java.util.Objects.equals(mTemplateParams, that.mTemplateParams);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mContent);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mTemplateId);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mTemplateParams);
+        return _hash;
+    }
+
+    /**
+     * A builder for {@link RenderOutput}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @Nullable String mContent;
+        private @Nullable String mTemplateId;
+        private @NonNull PersistableBundle mTemplateParams;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * The HTML content to be rendered in a webview. If this is null, the ODP service
+         * generates HTML from the data in {@link #getTemplateId()} and {@link #getTemplateParams()}
+         * as described below.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setContent(@Nullable String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mContent = value;
+            return this;
+        }
+
+        /**
+         * A key in the REMOTE_DATA {@link IsolatedService#getRemoteData(RequestToken)} table that
+         * points to an <a href="velocity.apache.org">Apache Velocity</a> template. This is ignored if
+         * {@link #getContent()} is not null.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setTemplateId(@Nullable String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mTemplateId = value;
+            return this;
+        }
+
+        /**
+         * The parameters to be populated in the template from {@link #getTemplateId()}. This is
+         * ignored if {@link #getContent()} is not null.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setTemplateParams(@NonNull PersistableBundle value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mTemplateParams = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull RenderOutput build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mContent = null;
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mTemplateId = null;
+            }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mTemplateParams = PersistableBundle.EMPTY;
+            }
+            RenderOutput o = new RenderOutput(
+                    mContent,
+                    mTemplateId,
+                    mTemplateParams);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x8) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1707253768205L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/RenderOutput.java",
+            inputSignatures = "private @com.android.ondevicepersonalization.internal.util.DataClass.MaySetToNull @android.annotation.Nullable java.lang.String mContent\nprivate @com.android.ondevicepersonalization.internal.util.DataClass.MaySetToNull @android.annotation.Nullable java.lang.String mTemplateId\nprivate @android.annotation.NonNull android.os.PersistableBundle mTemplateParams\nclass RenderOutput extends java.lang.Object implements []\[email protected](genBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/RenderOutputParcel.java b/android-35/android/adservices/ondevicepersonalization/RenderOutputParcel.java
new file mode 100644
index 0000000..afd4cfa
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/RenderOutputParcel.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * Parcelable version of {@link RenderOutput}.
+ * @hide
+ */
+@DataClass(genAidl = false, genBuilder = false)
+public final class RenderOutputParcel implements Parcelable {
+    /**
+     * The HTML content to be rendered in a webview. If this is null, the ODP service
+     * generates HTML from the data in {@link #getTemplateId()} and {@link #getTemplateParams()}
+     * as described below.
+     */
+    @Nullable private String mContent = null;
+
+    /**
+     * A key in the REMOTE_DATA {@link IsolatedService#getRemoteData(RequestToken)} table that
+     * points to an <a href="velocity.apache.org">Apache Velocity</a> template. This is ignored if
+     * {@link #getContent()} is not null.
+     */
+    @Nullable private String mTemplateId = null;
+
+    /**
+     * The parameters to be populated in the template from {@link #getTemplateId()}. This is
+     * ignored if {@link #getContent()} is not null.
+     */
+    @NonNull private PersistableBundle mTemplateParams = PersistableBundle.EMPTY;
+
+    /** @hide */
+    public RenderOutputParcel(@NonNull RenderOutput value) {
+        this(value.getContent(), value.getTemplateId(), value.getTemplateParams());
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/RenderOutputParcel.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new RenderOutputParcel.
+     *
+     * @param content
+     *   The HTML content to be rendered in a webview. If this is null, the ODP service
+     *   generates HTML from the data in {@link #getTemplateId()} and {@link #getTemplateParams()}
+     *   as described below.
+     * @param templateId
+     *   A key in the REMOTE_DATA {@link IsolatedService#getRemoteData(RequestToken)} table that
+     *   points to an <a href="velocity.apache.org">Apache Velocity</a> template. This is ignored if
+     *   {@link #getContent()} is not null.
+     * @param templateParams
+     *   The parameters to be populated in the template from {@link #getTemplateId()}. This is
+     *   ignored if {@link #getContent()} is not null.
+     */
+    @DataClass.Generated.Member
+    public RenderOutputParcel(
+            @Nullable String content,
+            @Nullable String templateId,
+            @NonNull PersistableBundle templateParams) {
+        this.mContent = content;
+        this.mTemplateId = templateId;
+        this.mTemplateParams = templateParams;
+        AnnotationValidations.validate(
+                NonNull.class, null, mTemplateParams);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The HTML content to be rendered in a webview. If this is null, the ODP service
+     * generates HTML from the data in {@link #getTemplateId()} and {@link #getTemplateParams()}
+     * as described below.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getContent() {
+        return mContent;
+    }
+
+    /**
+     * A key in the REMOTE_DATA {@link IsolatedService#getRemoteData(RequestToken)} table that
+     * points to an <a href="velocity.apache.org">Apache Velocity</a> template. This is ignored if
+     * {@link #getContent()} is not null.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getTemplateId() {
+        return mTemplateId;
+    }
+
+    /**
+     * The parameters to be populated in the template from {@link #getTemplateId()}. This is
+     * ignored if {@link #getContent()} is not null.
+     */
+    @DataClass.Generated.Member
+    public @NonNull PersistableBundle getTemplateParams() {
+        return mTemplateParams;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mContent != null) flg |= 0x1;
+        if (mTemplateId != null) flg |= 0x2;
+        dest.writeByte(flg);
+        if (mContent != null) dest.writeString(mContent);
+        if (mTemplateId != null) dest.writeString(mTemplateId);
+        dest.writeTypedObject(mTemplateParams, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ RenderOutputParcel(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        String content = (flg & 0x1) == 0 ? null : in.readString();
+        String templateId = (flg & 0x2) == 0 ? null : in.readString();
+        PersistableBundle templateParams = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR);
+
+        this.mContent = content;
+        this.mTemplateId = templateId;
+        this.mTemplateParams = templateParams;
+        AnnotationValidations.validate(
+                NonNull.class, null, mTemplateParams);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<RenderOutputParcel> CREATOR
+            = new Parcelable.Creator<RenderOutputParcel>() {
+        @Override
+        public RenderOutputParcel[] newArray(int size) {
+            return new RenderOutputParcel[size];
+        }
+
+        @Override
+        public RenderOutputParcel createFromParcel(@NonNull android.os.Parcel in) {
+            return new RenderOutputParcel(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1698864341247L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/RenderOutputParcel.java",
+            inputSignatures = "private @android.annotation.Nullable java.lang.String mContent\nprivate @android.annotation.Nullable java.lang.String mTemplateId\nprivate @android.annotation.NonNull android.os.PersistableBundle mTemplateParams\nclass RenderOutputParcel extends java.lang.Object implements [android.os.Parcelable]\[email protected](genAidl=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/RenderingConfig.java b/android-35/android/adservices/ondevicepersonalization/RenderingConfig.java
new file mode 100644
index 0000000..5e26adf
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/RenderingConfig.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Information returned by
+ * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}
+ * that is used in a subesequent call to
+ * {@link IsolatedWorker#onRender(RenderInput, android.os.OutcomeReceiver)} to identify the
+ * content to be displayed in a single {@link android.view.View}.
+ *
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public final class RenderingConfig implements Parcelable {
+    /**
+     * A List of keys in the REMOTE_DATA
+     * {@link IsolatedService#getRemoteData(RequestToken)}
+     * table that identify the content to be rendered.
+     **/
+    @DataClass.PluralOf("key")
+    @NonNull private List<String> mKeys = Collections.emptyList();
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/RenderingConfig.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ RenderingConfig(
+            @NonNull List<String> keys) {
+        this.mKeys = keys;
+        AnnotationValidations.validate(
+                NonNull.class, null, mKeys);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * A List of keys in the REMOTE_DATA
+     * {@link IsolatedService#getRemoteData(RequestToken)}
+     * table that identify the content to be rendered.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<String> getKeys() {
+        return mKeys;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(RenderingConfig other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        RenderingConfig that = (RenderingConfig) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mKeys, that.mKeys);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mKeys);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeStringList(mKeys);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ RenderingConfig(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        List<String> keys = new java.util.ArrayList<>();
+        in.readStringList(keys);
+
+        this.mKeys = keys;
+        AnnotationValidations.validate(
+                NonNull.class, null, mKeys);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<RenderingConfig> CREATOR
+            = new Parcelable.Creator<RenderingConfig>() {
+        @Override
+        public RenderingConfig[] newArray(int size) {
+            return new RenderingConfig[size];
+        }
+
+        @Override
+        public RenderingConfig createFromParcel(@NonNull android.os.Parcel in) {
+            return new RenderingConfig(in);
+        }
+    };
+
+    /**
+     * A builder for {@link RenderingConfig}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @NonNull List<String> mKeys;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * A List of keys in the REMOTE_DATA
+         * {@link IsolatedService#getRemoteData(RequestToken)}
+         * table that identify the content to be rendered.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setKeys(@NonNull List<String> value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mKeys = value;
+            return this;
+        }
+
+        /** @see #setKeys */
+        @DataClass.Generated.Member
+        public @NonNull Builder addKey(@NonNull String value) {
+            if (mKeys == null) setKeys(new java.util.ArrayList<>());
+            mKeys.add(value);
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull RenderingConfig build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mKeys = Collections.emptyList();
+            }
+            RenderingConfig o = new RenderingConfig(
+                    mKeys);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x2) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1697132616124L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/RenderingConfig.java",
+            inputSignatures = "private @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"key\") @android.annotation.NonNull java.util.List<java.lang.String> mKeys\nclass RenderingConfig extends java.lang.Object implements [android.os.Parcelable]\[email protected](genBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/RequestLogRecord.java b/android-35/android/adservices/ondevicepersonalization/RequestLogRecord.java
new file mode 100644
index 0000000..8ec3409
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/RequestLogRecord.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.content.ContentValues;
+import android.os.Parcelable;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+import java.time.Instant;
+import java.util.Collections;
+import java.util.List;
+
+// TODO(b/289102463): Add a link to the public doc for the REQUESTS table when available.
+/**
+ * Contains data that will be written to the REQUESTS table at the end of a call to
+ * {@link IsolatedWorker#onExecute(ExecuteInput, android.os.OutcomeReceiver)}.
+ * A single {@link RequestLogRecord} is appended to the
+ * REQUESTS table if it is present in the output of one of the methods in {@link IsolatedWorker}.
+ * The contents of the REQUESTS table can be consumed by Federated Learning facilitated model
+ * training, or Federated Analytics facilitated cross-device statistical analysis.
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public final class RequestLogRecord implements Parcelable {
+    /**
+     * A List of rows, each containing a {@link ContentValues}.
+     **/
+    @DataClass.PluralOf("row")
+    @NonNull List<ContentValues> mRows = Collections.emptyList();
+
+    /**
+     * Internal id for the RequestLogRecord.
+     * @hide
+     */
+    private long mRequestId = 0;
+
+    /**
+     * Time of the request in milliseconds
+     * @hide
+     */
+    private long mTimeMillis = 0;
+
+    /**
+     * Returns the timestamp of this record.
+     */
+    @NonNull public Instant getTime() {
+        return Instant.ofEpochMilli(getTimeMillis());
+    }
+
+    abstract static class BaseBuilder {
+        /**
+         * @hide
+         */
+        public abstract Builder setTimeMillis(long value);
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/RequestLogRecord.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ RequestLogRecord(
+            @NonNull List<ContentValues> rows,
+            long requestId,
+            long timeMillis) {
+        this.mRows = rows;
+        AnnotationValidations.validate(
+                NonNull.class, null, mRows);
+        this.mRequestId = requestId;
+        this.mTimeMillis = timeMillis;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * A List of rows, each containing a {@link ContentValues}.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<ContentValues> getRows() {
+        return mRows;
+    }
+
+    /**
+     * Internal id for the RequestLogRecord.
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public long getRequestId() {
+        return mRequestId;
+    }
+
+    /**
+     * Time of the request in milliseconds
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public long getTimeMillis() {
+        return mTimeMillis;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(RequestLogRecord other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        RequestLogRecord that = (RequestLogRecord) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mRows, that.mRows)
+                && mRequestId == that.mRequestId
+                && mTimeMillis == that.mTimeMillis;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mRows);
+        _hash = 31 * _hash + Long.hashCode(mRequestId);
+        _hash = 31 * _hash + Long.hashCode(mTimeMillis);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeParcelableList(mRows, flags);
+        dest.writeLong(mRequestId);
+        dest.writeLong(mTimeMillis);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ RequestLogRecord(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        List<ContentValues> rows = new java.util.ArrayList<>();
+        in.readParcelableList(rows, ContentValues.class.getClassLoader());
+        long requestId = in.readLong();
+        long timeMillis = in.readLong();
+
+        this.mRows = rows;
+        AnnotationValidations.validate(
+                NonNull.class, null, mRows);
+        this.mRequestId = requestId;
+        this.mTimeMillis = timeMillis;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<RequestLogRecord> CREATOR
+            = new Parcelable.Creator<RequestLogRecord>() {
+        @Override
+        public RequestLogRecord[] newArray(int size) {
+            return new RequestLogRecord[size];
+        }
+
+        @Override
+        public RequestLogRecord createFromParcel(@NonNull android.os.Parcel in) {
+            return new RequestLogRecord(in);
+        }
+    };
+
+    /**
+     * A builder for {@link RequestLogRecord}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder extends BaseBuilder {
+
+        private @NonNull List<ContentValues> mRows;
+        private long mRequestId;
+        private long mTimeMillis;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * A List of rows, each containing a {@link ContentValues}.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setRows(@NonNull List<ContentValues> value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mRows = value;
+            return this;
+        }
+
+        /** @see #setRows */
+        @DataClass.Generated.Member
+        public @NonNull Builder addRow(@NonNull ContentValues value) {
+            if (mRows == null) setRows(new java.util.ArrayList<>());
+            mRows.add(value);
+            return this;
+        }
+
+        /**
+         * Internal id for the RequestLogRecord.
+         *
+         * @hide
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setRequestId(long value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mRequestId = value;
+            return this;
+        }
+
+        /**
+         * Time of the request in milliseconds
+         *
+         * @hide
+         */
+        @DataClass.Generated.Member
+        @Override
+        public @NonNull Builder setTimeMillis(long value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mTimeMillis = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull RequestLogRecord build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mRows = Collections.emptyList();
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mRequestId = 0;
+            }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mTimeMillis = 0;
+            }
+            RequestLogRecord o = new RequestLogRecord(
+                    mRows,
+                    mRequestId,
+                    mTimeMillis);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x8) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1698962042612L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/RequestLogRecord.java",
+            inputSignatures = " @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"row\") @android.annotation.NonNull java.util.List<android.content.ContentValues> mRows\nprivate  long mRequestId\nprivate  long mTimeMillis\npublic @android.annotation.NonNull java.time.Instant getTime()\nclass RequestLogRecord extends java.lang.Object implements [android.os.Parcelable]\npublic abstract  android.adservices.ondevicepersonalization.RequestLogRecord.Builder setTimeMillis(long)\nclass BaseBuilder extends java.lang.Object implements []\[email protected](genBuilder=true, genEqualsHashCode=true)\npublic abstract  android.adservices.ondevicepersonalization.RequestLogRecord.Builder setTimeMillis(long)\nclass BaseBuilder extends java.lang.Object implements []")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/RequestToken.java b/android-35/android/adservices/ondevicepersonalization/RequestToken.java
new file mode 100644
index 0000000..89f4512
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/RequestToken.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.adservices.ondevicepersonalization.aidl.IDataAccessService;
+import android.adservices.ondevicepersonalization.aidl.IFederatedComputeService;
+import android.adservices.ondevicepersonalization.aidl.IIsolatedModelService;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.SystemClock;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+
+import java.util.Objects;
+
+/**
+ * An opaque token that identifies the current request to an {@link IsolatedService}. This token
+ * must be passed as a parameter to all service methods that depend on per-request state.
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public class RequestToken {
+    @NonNull
+    private final IDataAccessService mDataAccessService;
+
+    @Nullable
+    private final IFederatedComputeService mFcService;
+
+    @Nullable private final IIsolatedModelService mModelService;
+
+    @Nullable
+    private final UserData mUserData;
+
+    private final long mStartTimeMillis;
+
+    /** @hide */
+    RequestToken(
+            @NonNull IDataAccessService binder,
+            @Nullable IFederatedComputeService fcServiceBinder,
+            @Nullable IIsolatedModelService modelServiceBinder,
+            @Nullable UserData userData) {
+        mDataAccessService = Objects.requireNonNull(binder);
+        mFcService = fcServiceBinder;
+        mModelService = modelServiceBinder;
+        mUserData = userData;
+        mStartTimeMillis = SystemClock.elapsedRealtime();
+    }
+
+    /** @hide */
+    @NonNull
+    IDataAccessService getDataAccessService() {
+        return mDataAccessService;
+    }
+
+    /** @hide */
+    @Nullable
+    IFederatedComputeService getFederatedComputeService() {
+        return mFcService;
+    }
+
+    /** @hide */
+    @Nullable
+    IIsolatedModelService getModelService() {
+        return mModelService;
+    }
+
+    /** @hide */
+    @Nullable
+    UserData getUserData() {
+        return mUserData;
+    }
+
+    /** @hide */
+    long getStartTimeMillis() {
+        return mStartTimeMillis;
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/SurfacePackageToken.java b/android-35/android/adservices/ondevicepersonalization/SurfacePackageToken.java
new file mode 100644
index 0000000..d412de9
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/SurfacePackageToken.java
@@ -0,0 +1,43 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+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
+ * maps to a {@link RenderingConfig} returned by an {@link IsolatedService}.
+ *
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+public class SurfacePackageToken {
+    @NonNull private final String mTokenString;
+
+    SurfacePackageToken(@NonNull String tokenString) {
+        mTokenString = tokenString;
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    @NonNull public String getTokenString() {
+        return mTokenString;
+    }
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/TrainingExampleRecord.java b/android-35/android/adservices/ondevicepersonalization/TrainingExampleRecord.java
new file mode 100644
index 0000000..f9bfa4a
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/TrainingExampleRecord.java
@@ -0,0 +1,215 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+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(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = true, genAidl = false)
+public final class TrainingExampleRecord implements Parcelable {
+    /**
+     * Training example byte arrays. The format is a binary serialized <a
+     * href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/example.proto">
+     * tensorflow.Example</a> proto. The maximum allowed example size is 50KB.
+     */
+    @DataClass.MaySetToNull
+    @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.
+     */
+    @DataClass.MaySetToNull
+    @Nullable private byte[] mResumptionToken = null;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExampleRecord.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ TrainingExampleRecord(
+            @Nullable byte[] trainingExample,
+            @Nullable byte[] resumptionToken) {
+        this.mTrainingExample = trainingExample;
+        this.mResumptionToken = resumptionToken;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Training example byte arrays. The format is a binary serialized <a
+     * href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/example.proto">
+     * tensorflow.Example</a> proto. The maximum allowed example size is 50KB.
+     */
+    @DataClass.Generated.Member
+    public @Nullable byte[] getTrainingExample() {
+        return mTrainingExample;
+    }
+
+    /**
+     * 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.
+     */
+    @DataClass.Generated.Member
+    public @Nullable byte[] getResumptionToken() {
+        return mResumptionToken;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeByteArray(mTrainingExample);
+        dest.writeByteArray(mResumptionToken);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ TrainingExampleRecord(@android.annotation.NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte[] trainingExample = in.createByteArray();
+        byte[] resumptionToken = in.createByteArray();
+
+        this.mTrainingExample = trainingExample;
+        this.mResumptionToken = resumptionToken;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @android.annotation.NonNull Parcelable.Creator<TrainingExampleRecord> CREATOR
+            = new Parcelable.Creator<TrainingExampleRecord>() {
+        @Override
+        public TrainingExampleRecord[] newArray(int size) {
+            return new TrainingExampleRecord[size];
+        }
+
+        @Override
+        public TrainingExampleRecord createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
+            return new TrainingExampleRecord(in);
+        }
+    };
+
+    /**
+     * A builder for {@link TrainingExampleRecord}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @Nullable byte[] mTrainingExample;
+        private @Nullable byte[] mResumptionToken;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * Training example byte arrays. The format is a binary serialized <a
+         * href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/example.proto">
+         * tensorflow.Example</a> proto. The maximum allowed example size is 50KB.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setTrainingExample(@Nullable byte... value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mTrainingExample = value;
+            return this;
+        }
+
+        /**
+         * 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.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setResumptionToken(@Nullable byte... value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mResumptionToken = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @android.annotation.NonNull TrainingExampleRecord build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mTrainingExample = null;
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mResumptionToken = null;
+            }
+            TrainingExampleRecord o = new TrainingExampleRecord(
+                    mTrainingExample,
+                    mResumptionToken);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x4) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1707253849218L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExampleRecord.java",
+            inputSignatures = "private @com.android.ondevicepersonalization.internal.util.DataClass.MaySetToNull @android.annotation.Nullable byte[] mTrainingExample\nprivate @com.android.ondevicepersonalization.internal.util.DataClass.MaySetToNull @android.annotation.Nullable byte[] mResumptionToken\nclass TrainingExampleRecord extends java.lang.Object implements [android.os.Parcelable]\[email protected](genBuilder=true, genAidl=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/TrainingExamplesInput.java b/android-35/android/adservices/ondevicepersonalization/TrainingExamplesInput.java
new file mode 100644
index 0000000..7fe868c
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/TrainingExamplesInput.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/** The input data for {@link IsolatedWorker#onTrainingExamples}. */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = false, genHiddenConstructor = true, genEqualsHashCode = true)
+public final class TrainingExamplesInput {
+    /**
+     * The name of the federated compute population. It should match the population name in {@link
+     * FederatedComputeInput#getPopulationName}.
+     */
+    @NonNull private String mPopulationName = "";
+
+    /**
+     * The name of the task within the population. It should match task plan configured at remote
+     * federated compute server. One population may have multiple tasks. The task name can be used
+     * to uniquely identify the job.
+     */
+    @NonNull private String mTaskName = "";
+
+    /**
+     * Token used to support the resumption of training. If client app wants to use resumption token
+     * to track what examples are already used in previous federated compute jobs, it need set
+     * {@link TrainingExampleRecord.Builder#setResumptionToken}, OnDevicePersonalization will store
+     * it and pass it here for generating new training examples.
+     */
+    @Nullable private byte[] mResumptionToken = null;
+
+    /**
+     * The data collection name to use to create training examples.
+     *
+     * @hide
+     */
+    @Nullable private String mCollectionUri;
+
+    /** @hide */
+    public TrainingExamplesInput(@NonNull TrainingExamplesInputParcel parcel) {
+        this(
+                parcel.getPopulationName(),
+                parcel.getTaskName(),
+                parcel.getResumptionToken(),
+                parcel.getCollectionUri());
+    }
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen
+    // $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesInput.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    // @formatter:off
+
+    /**
+     * Creates a new TrainingExamplesInput.
+     *
+     * @param populationName The name of the federated compute population. It should match the
+     *     population name in {@link FederatedComputeInput#getPopulationName}.
+     * @param taskName The name of the task within the population. It should match task plan
+     *     configured at remote federated compute server. One population may have multiple tasks.
+     *     The task name can be used to uniquely identify the job.
+     * @param resumptionToken Token used to support the resumption of training. If client app wants
+     *     to use resumption token to track what examples are already used in previous federated
+     *     compute jobs, it need set {@link TrainingExampleRecord.Builder#setResumptionToken},
+     *     OnDevicePersonalization will store it and pass it here for generating new training
+     *     examples.
+     * @param collectionUri The data collection name to use to create training examples.
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public TrainingExamplesInput(
+            @NonNull String populationName,
+            @NonNull String taskName,
+            @Nullable byte[] resumptionToken,
+            @Nullable String collectionUri) {
+        this.mPopulationName = populationName;
+        AnnotationValidations.validate(NonNull.class, null, mPopulationName);
+        this.mTaskName = taskName;
+        AnnotationValidations.validate(NonNull.class, null, mTaskName);
+        this.mResumptionToken = resumptionToken;
+        this.mCollectionUri = collectionUri;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The name of the federated compute population. It should match the population name in {@link
+     * FederatedComputeInput#getPopulationName}.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getPopulationName() {
+        return mPopulationName;
+    }
+
+    /**
+     * The name of the task within the population. It should match task plan configured at remote
+     * federated compute server. One population may have multiple tasks. The task name can be used
+     * to uniquely identify the job.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getTaskName() {
+        return mTaskName;
+    }
+
+    /**
+     * Token used to support the resumption of training. If client app wants to use resumption token
+     * to track what examples are already used in previous federated compute jobs, it need set
+     * {@link TrainingExampleRecord.Builder#setResumptionToken}, OnDevicePersonalization will store
+     * it and pass it here for generating new training examples.
+     */
+    @DataClass.Generated.Member
+    public @Nullable byte[] getResumptionToken() {
+        return mResumptionToken;
+    }
+
+    /**
+     * The data collection name to use to create training examples.
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getCollectionUri() {
+        return mCollectionUri;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(TrainingExamplesInput other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        TrainingExamplesInput that = (TrainingExamplesInput) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mPopulationName, that.mPopulationName)
+                && java.util.Objects.equals(mTaskName, that.mTaskName)
+                && java.util.Arrays.equals(mResumptionToken, that.mResumptionToken)
+                && java.util.Objects.equals(mCollectionUri, that.mCollectionUri);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mPopulationName);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mTaskName);
+        _hash = 31 * _hash + java.util.Arrays.hashCode(mResumptionToken);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mCollectionUri);
+        return _hash;
+    }
+
+    @DataClass.Generated(
+            time = 1714844498373L,
+            codegenVersion = "1.0.23",
+            sourceFile =
+                    "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesInput.java",
+            inputSignatures =
+                    "private @android.annotation.NonNull java.lang.String mPopulationName\nprivate @android.annotation.NonNull java.lang.String mTaskName\nprivate @android.annotation.Nullable byte[] mResumptionToken\nprivate @android.annotation.Nullable java.lang.String mCollectionUri\nclass TrainingExamplesInput extends java.lang.Object implements []\[email protected](genBuilder=false, genHiddenConstructor=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+    // @formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/TrainingExamplesInputParcel.java b/android-35/android/adservices/ondevicepersonalization/TrainingExamplesInputParcel.java
new file mode 100644
index 0000000..a039a3b
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/TrainingExamplesInputParcel.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * Parcelable version of {@link TrainingExamplesInput}
+ *
+ * @hide
+ */
+@DataClass(genAidl = false, genHiddenBuilder = true)
+public final class TrainingExamplesInputParcel implements Parcelable {
+    /** The name of the federated compute population. */
+    @NonNull private String mPopulationName = "";
+
+    /**
+     * The name of the task within the population. One population may have multiple tasks. The task
+     * name can be used to uniquely identify the job.
+     */
+    @NonNull private String mTaskName = "";
+
+    /** Token used to support the resumption of training. */
+    @Nullable private byte[] mResumptionToken = null;
+
+    /** The data collection name to use to create training examples. */
+    @Nullable private String mCollectionUri;
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen
+    // $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesInputParcel.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    // @formatter:off
+
+    @DataClass.Generated.Member
+    /* package-private */ TrainingExamplesInputParcel(
+            @NonNull String populationName,
+            @NonNull String taskName,
+            @Nullable byte[] resumptionToken,
+            @Nullable String collectionUri) {
+        this.mPopulationName = populationName;
+        AnnotationValidations.validate(NonNull.class, null, mPopulationName);
+        this.mTaskName = taskName;
+        AnnotationValidations.validate(NonNull.class, null, mTaskName);
+        this.mResumptionToken = resumptionToken;
+        this.mCollectionUri = collectionUri;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The name of the federated compute population.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getPopulationName() {
+        return mPopulationName;
+    }
+
+    /**
+     * The name of the task within the population. One population may have multiple tasks. The task
+     * name can be used to uniquely identify the job.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getTaskName() {
+        return mTaskName;
+    }
+
+    /**
+     * Token used to support the resumption of training.
+     */
+    @DataClass.Generated.Member
+    public @Nullable byte[] getResumptionToken() {
+        return mResumptionToken;
+    }
+
+    /** The data collection name to use to create training examples. */
+    @DataClass.Generated.Member
+    public @Nullable String getCollectionUri() {
+        return mCollectionUri;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mCollectionUri != null) flg |= 0x8;
+        dest.writeByte(flg);
+        dest.writeString(mPopulationName);
+        dest.writeString(mTaskName);
+        dest.writeByteArray(mResumptionToken);
+        if (mCollectionUri != null) dest.writeString(mCollectionUri);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ TrainingExamplesInputParcel(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        String populationName = in.readString();
+        String taskName = in.readString();
+        byte[] resumptionToken = in.createByteArray();
+        String collectionUri = (flg & 0x8) == 0 ? null : in.readString();
+
+        this.mPopulationName = populationName;
+        AnnotationValidations.validate(NonNull.class, null, mPopulationName);
+        this.mTaskName = taskName;
+        AnnotationValidations.validate(
+                NonNull.class, null, mTaskName);
+        this.mResumptionToken = resumptionToken;
+        this.mCollectionUri = collectionUri;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<TrainingExamplesInputParcel> CREATOR =
+            new Parcelable.Creator<TrainingExamplesInputParcel>() {
+                @Override
+                public TrainingExamplesInputParcel[] newArray(int size) {
+                    return new TrainingExamplesInputParcel[size];
+                }
+
+                @Override
+                public TrainingExamplesInputParcel createFromParcel(@NonNull android.os.Parcel in) {
+                    return new TrainingExamplesInputParcel(in);
+                }
+            };
+
+    /**
+     * A builder for {@link TrainingExamplesInputParcel}
+     * @hide
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @NonNull String mPopulationName;
+        private @NonNull String mTaskName;
+        private @Nullable byte[] mResumptionToken;
+        private @Nullable String mCollectionUri;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {}
+
+        /**
+         * The name of the federated compute population.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setPopulationName(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mPopulationName = value;
+            return this;
+        }
+
+        /**
+         * The name of the task within the population. One population may have multiple tasks. The
+         * task name can be used to uniquely identify the job.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setTaskName(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mTaskName = value;
+            return this;
+        }
+
+        /**
+         * Token used to support the resumption of training.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setResumptionToken(@NonNull byte... value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mResumptionToken = value;
+            return this;
+        }
+
+        /** The data collection name to use to create training examples. */
+        @DataClass.Generated.Member
+        public @NonNull Builder setCollectionUri(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mCollectionUri = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull TrainingExamplesInputParcel build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mPopulationName = "";
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mTaskName = "";
+            }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mResumptionToken = null;
+            }
+            TrainingExamplesInputParcel o =
+                    new TrainingExamplesInputParcel(
+                            mPopulationName, mTaskName, mResumptionToken, mCollectionUri);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x10) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1714844524307L,
+            codegenVersion = "1.0.23",
+            sourceFile =
+                    "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesInputParcel.java",
+            inputSignatures =
+                    "private @android.annotation.NonNull java.lang.String mPopulationName\nprivate @android.annotation.NonNull java.lang.String mTaskName\nprivate @android.annotation.Nullable byte[] mResumptionToken\nprivate @android.annotation.Nullable java.lang.String mCollectionUri\nclass TrainingExamplesInputParcel extends java.lang.Object implements [android.os.Parcelable]\[email protected](genAidl=false, genHiddenBuilder=true)")
+    @Deprecated
+    private void __metadata() {}
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/TrainingExamplesOutput.java b/android-35/android/adservices/ondevicepersonalization/TrainingExamplesOutput.java
new file mode 100644
index 0000000..662c011
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/TrainingExamplesOutput.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.internal.util.Preconditions;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+import java.util.Collections;
+import java.util.List;
+
+/** The output data of {@link IsolatedWorker#onTrainingExamples} */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public final class TrainingExamplesOutput {
+    /**
+     * The list of training example byte arrays. The format is a binary serialized <a
+     * href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/example.proto">
+     * tensorflow.Example</a> proto. The maximum allowed example size is 50KB.
+     */
+    @NonNull
+    @DataClass.PluralOf("trainingExampleRecord")
+    private List<TrainingExampleRecord> mTrainingExampleRecords = Collections.emptyList();
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen
+    // $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesOutput.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    // @formatter:off
+
+    @DataClass.Generated.Member
+    /* package-private */ TrainingExamplesOutput(
+            @NonNull List<TrainingExampleRecord> trainingExampleRecords) {
+        this.mTrainingExampleRecords = trainingExampleRecords;
+        AnnotationValidations.validate(NonNull.class, null, mTrainingExampleRecords);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The list of training example byte arrays. The format is a binary serialized <a
+     * href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/example.proto">
+     * tensorflow.Example</a> proto. The maximum allowed example size is 50KB.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<TrainingExampleRecord> getTrainingExampleRecords() {
+        return mTrainingExampleRecords;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(TrainingExamplesOutput other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        TrainingExamplesOutput that = (TrainingExamplesOutput) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mTrainingExampleRecords, that.mTrainingExampleRecords);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mTrainingExampleRecords);
+        return _hash;
+    }
+
+    /** A builder for {@link TrainingExamplesOutput} */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @NonNull List<TrainingExampleRecord> mTrainingExampleRecords;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {}
+
+        /**
+         * The list of training example byte arrays. The format is a binary serialized <a
+         * href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/example.proto">
+         * tensorflow.Example</a> proto. The maximum allowed example size is 50KB.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setTrainingExampleRecords(
+                @NonNull List<TrainingExampleRecord> value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mTrainingExampleRecords = value;
+            return this;
+        }
+
+        /**
+         * @see #setTrainingExampleRecords
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder addTrainingExampleRecord(@NonNull TrainingExampleRecord value) {
+            if (mTrainingExampleRecords == null)
+                setTrainingExampleRecords(new java.util.ArrayList<>());
+            mTrainingExampleRecords.add(value);
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull TrainingExamplesOutput build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mTrainingExampleRecords = Collections.emptyList();
+            }
+            TrainingExamplesOutput o = new TrainingExamplesOutput(mTrainingExampleRecords);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x2) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1704915709729L,
+            codegenVersion = "1.0.23",
+            sourceFile =
+                    "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesOutput.java",
+            inputSignatures =
+                    "private @android.annotation.NonNull"
+                        + " @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"trainingExampleRecord\")"
+                        + " java.util.List<android.adservices.ondevicepersonalization.TrainingExampleRecord>"
+                        + " mTrainingExampleRecords\n"
+                        + "class TrainingExamplesOutput extends java.lang.Object implements []\n"
+                        + "@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true,"
+                        + " genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+    // @formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/TrainingExamplesOutputParcel.java b/android-35/android/adservices/ondevicepersonalization/TrainingExamplesOutputParcel.java
new file mode 100644
index 0000000..f621565
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/TrainingExamplesOutputParcel.java
@@ -0,0 +1,206 @@
+/*
+ * 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.Nullable;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.DataClass;
+import com.android.ondevicepersonalization.internal.util.OdpParceledListSlice;
+
+/**
+ * Parcelable version of {@link TrainingExamplesOutput}
+ *
+ * @hide
+ */
+@DataClass(genAidl = false, genHiddenBuilder = true, genEqualsHashCode = true)
+public class TrainingExamplesOutputParcel implements Parcelable {
+    /** List of training example records. */
+    @Nullable OdpParceledListSlice<TrainingExampleRecord> mTrainingExampleRecords = null;
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen
+    // $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesOutputParcel.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    // @formatter:off
+
+    @DataClass.Generated.Member
+    /* package-private */ TrainingExamplesOutputParcel(
+            @Nullable OdpParceledListSlice<TrainingExampleRecord> trainingExampleRecords) {
+        this.mTrainingExampleRecords = trainingExampleRecords;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /** List of training example records. */
+    @DataClass.Generated.Member
+    public @Nullable OdpParceledListSlice<TrainingExampleRecord> getTrainingExampleRecords() {
+        return mTrainingExampleRecords;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(TrainingExamplesOutputParcel other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        TrainingExamplesOutputParcel that = (TrainingExamplesOutputParcel) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mTrainingExampleRecords, that.mTrainingExampleRecords);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mTrainingExampleRecords);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mTrainingExampleRecords != null) flg |= 0x1;
+        dest.writeByte(flg);
+        if (mTrainingExampleRecords != null) dest.writeTypedObject(mTrainingExampleRecords, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected TrainingExamplesOutputParcel(@android.annotation.NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        OdpParceledListSlice<TrainingExampleRecord> trainingExampleRecords =
+                (flg & 0x1) == 0
+                        ? null
+                        : (OdpParceledListSlice) in.readTypedObject(OdpParceledListSlice.CREATOR);
+
+        this.mTrainingExampleRecords = trainingExampleRecords;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @android.annotation.NonNull Parcelable.Creator<TrainingExamplesOutputParcel>
+            CREATOR =
+                    new Parcelable.Creator<TrainingExamplesOutputParcel>() {
+                        @Override
+                        public TrainingExamplesOutputParcel[] newArray(int size) {
+                            return new TrainingExamplesOutputParcel[size];
+                        }
+
+                        @Override
+                        public TrainingExamplesOutputParcel createFromParcel(
+                                @android.annotation.NonNull android.os.Parcel in) {
+                            return new TrainingExamplesOutputParcel(in);
+                        }
+                    };
+
+    /**
+     * A builder for {@link TrainingExamplesOutputParcel}
+     *
+     * @hide
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static class Builder {
+
+        private @Nullable OdpParceledListSlice<TrainingExampleRecord> mTrainingExampleRecords;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {}
+
+        /** List of training example records. */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setTrainingExampleRecords(
+                @android.annotation.NonNull OdpParceledListSlice<TrainingExampleRecord> value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mTrainingExampleRecords = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @android.annotation.NonNull TrainingExamplesOutputParcel build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mTrainingExampleRecords = null;
+            }
+            TrainingExamplesOutputParcel o =
+                    new TrainingExamplesOutputParcel(mTrainingExampleRecords);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x2) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1704916269933L,
+            codegenVersion = "1.0.23",
+            sourceFile =
+                    "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesOutputParcel.java",
+            inputSignatures =
+                    " @android.annotation.Nullable"
+                        + " com.android.ondevicepersonalization.internal.util.OdpParceledListSlice<android.adservices.ondevicepersonalization.TrainingExampleRecord>"
+                        + " mTrainingExampleRecords\n"
+                        + "class TrainingExamplesOutputParcel extends java.lang.Object implements"
+                        + " [android.os.Parcelable]\n"
+                        + "@com.android.ondevicepersonalization.internal.util.DataClass(genAidl=false,"
+                        + " genHiddenBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+    // @formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/TrainingInterval.java b/android-35/android/adservices/ondevicepersonalization/TrainingInterval.java
new file mode 100644
index 0000000..89c5b6d
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/TrainingInterval.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+import java.time.Duration;
+
+/** Training interval settings required for federated computation jobs. */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = true, genHiddenConstDefs = true, genEqualsHashCode = true)
+public final class TrainingInterval {
+    /** The scheduling mode for a one-off task. */
+    public static final int SCHEDULING_MODE_ONE_TIME = 1;
+
+    /** The scheduling mode for a task that will be rescheduled after each run. */
+    public static final int SCHEDULING_MODE_RECURRENT = 2;
+
+    /**
+     * The scheduling mode for this task, either {@link #SCHEDULING_MODE_ONE_TIME} or {@link
+     * #SCHEDULING_MODE_RECURRENT}. The default scheduling mode is {@link #SCHEDULING_MODE_ONE_TIME}
+     * if unspecified.
+     */
+    @SchedulingMode private int mSchedulingMode = SCHEDULING_MODE_ONE_TIME;
+
+    /**
+     * Sets the minimum time interval between two training runs.
+     *
+     * <p>This field will only be used when the scheduling mode is {@link
+     * #SCHEDULING_MODE_RECURRENT}. The value has be greater than zero.
+     *
+     * <p>Please also note this value is advisory, which does not guarantee the job will be run
+     * immediately after the interval expired. Federated compute will still enforce a minimum
+     * required interval and training constraints to ensure system health. The current training
+     * constraints are device on unmetered network, idle and battery not low.
+     */
+    @NonNull private Duration mMinimumInterval = Duration.ZERO;
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen
+    // $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingInterval.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    // @formatter:off
+
+    /** @hide */
+    @android.annotation.IntDef(
+            prefix = "SCHEDULING_MODE_",
+            value = {SCHEDULING_MODE_ONE_TIME, SCHEDULING_MODE_RECURRENT})
+    @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+    @DataClass.Generated.Member
+    public @interface SchedulingMode {}
+
+    /** @hide */
+    @DataClass.Generated.Member
+    public static String schedulingModeToString(@SchedulingMode int value) {
+        switch (value) {
+            case SCHEDULING_MODE_ONE_TIME:
+                return "SCHEDULING_MODE_ONE_TIME";
+            case SCHEDULING_MODE_RECURRENT:
+                return "SCHEDULING_MODE_RECURRENT";
+            default:
+                return Integer.toHexString(value);
+        }
+    }
+
+    @DataClass.Generated.Member
+    /* package-private */ TrainingInterval(
+            @SchedulingMode int schedulingMode, @NonNull Duration minimumInterval) {
+        this.mSchedulingMode = schedulingMode;
+
+        if (!(mSchedulingMode == SCHEDULING_MODE_ONE_TIME)
+                && !(mSchedulingMode == SCHEDULING_MODE_RECURRENT)) {
+            throw new java.lang.IllegalArgumentException(
+                    "schedulingMode was "
+                            + mSchedulingMode
+                            + " but must be one of: "
+                            + "SCHEDULING_MODE_ONE_TIME("
+                            + SCHEDULING_MODE_ONE_TIME
+                            + "), "
+                            + "SCHEDULING_MODE_RECURRENT("
+                            + SCHEDULING_MODE_RECURRENT
+                            + ")");
+        }
+
+        this.mMinimumInterval = minimumInterval;
+        AnnotationValidations.validate(NonNull.class, null, mMinimumInterval);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The scheduling mode for this task, either {@link #SCHEDULING_MODE_ONE_TIME} or {@link
+     * #SCHEDULING_MODE_RECURRENT}. The default scheduling mode is {@link #SCHEDULING_MODE_ONE_TIME}
+     * if unspecified.
+     */
+    @DataClass.Generated.Member
+    public @SchedulingMode int getSchedulingMode() {
+        return mSchedulingMode;
+    }
+
+    /**
+     * Sets the minimum time interval between two training runs.
+     *
+     * <p>This field will only be used when the scheduling mode is {@link
+     * #SCHEDULING_MODE_RECURRENT}. Only positive values are accepted, zero or negative values will
+     * result in IllegalArgumentException.
+     *
+     * <p>Please also note this value is advisory, which does not guarantee the job will be run
+     * immediately after the interval expired. Federated compute will still enforce a minimum
+     * required interval and training constraints to ensure system health. The current training
+     * constraints are device on unmetered network, idle and battery not low.
+     */
+    @DataClass.Generated.Member
+    public @NonNull Duration getMinimumInterval() {
+        return mMinimumInterval;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(TrainingInterval other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        TrainingInterval that = (TrainingInterval) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && mSchedulingMode == that.mSchedulingMode
+                && java.util.Objects.equals(mMinimumInterval, that.mMinimumInterval);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + mSchedulingMode;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mMinimumInterval);
+        return _hash;
+    }
+
+    /** A builder for {@link TrainingInterval} */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @SchedulingMode int mSchedulingMode;
+        private @NonNull Duration mMinimumInterval;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {}
+
+        /**
+         * The scheduling mode for this task, either {@link #SCHEDULING_MODE_ONE_TIME} or {@link
+         * #SCHEDULING_MODE_RECURRENT}. The default scheduling mode is {@link
+         * #SCHEDULING_MODE_ONE_TIME} if unspecified.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setSchedulingMode(@SchedulingMode int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mSchedulingMode = value;
+            return this;
+        }
+
+        /**
+         * Sets the minimum time interval between two training runs.
+         *
+         * <p>This field will only be used when the scheduling mode is {@link
+         * #SCHEDULING_MODE_RECURRENT}. Only positive values are accepted, zero or negative values
+         * will result in IllegalArgumentException.
+         *
+         * <p>Please also note this value is advisory, which does not guarantee the job will be run
+         * immediately after the interval expired. Federated compute will still enforce a minimum
+         * required interval and training constraints to ensure system health. The current training
+         * constraints are device on unmetered network, idle and battery not low.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setMinimumInterval(@NonNull Duration value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mMinimumInterval = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull TrainingInterval build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mSchedulingMode = SCHEDULING_MODE_ONE_TIME;
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mMinimumInterval = Duration.ZERO;
+            }
+            TrainingInterval o = new TrainingInterval(mSchedulingMode, mMinimumInterval);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x4) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1697653739724L,
+            codegenVersion = "1.0.23",
+            sourceFile =
+                    "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingInterval.java",
+            inputSignatures =
+                    "public static final  int SCHEDULING_MODE_ONE_TIME\npublic static final  int SCHEDULING_MODE_RECURRENT\nprivate @android.adservices.ondevicepersonalization.TrainingInterval.SchedulingMode int mSchedulingMode\nprivate @android.annotation.NonNull java.time.Duration mMinimumInterval\nclass TrainingInterval extends java.lang.Object implements []\[email protected](genBuilder=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+    // @formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/UserData.java b/android-35/android/adservices/ondevicepersonalization/UserData.java
new file mode 100644
index 0000000..a3a84a0
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/UserData.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
+import android.os.Parcelable;
+import android.telephony.TelephonyManager;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * User data provided by the platform to an {@link IsolatedService}.
+ *
+ */
+// This class should be updated with the Kotlin mirror
+// {@link com.android.ondevicepersonalization.services.policyengine.data.UserData}.
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genHiddenBuilder = true, genEqualsHashCode = true, genConstDefs = false)
+public final class UserData implements Parcelable {
+    /**
+     * The device timezone +/- offset from UTC.
+     *
+     * @hide
+     */
+    int mTimezoneUtcOffsetMins = 0;
+
+    /** @hide **/
+    @IntDef(prefix = {"ORIENTATION_"}, value = {
+            ORIENTATION_UNDEFINED,
+            ORIENTATION_PORTRAIT,
+            ORIENTATION_LANDSCAPE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Orientation {
+    }
+
+    /**
+     * The device orientation. The value can be one of the constants ORIENTATION_UNDEFINED,
+     * ORIENTATION_PORTRAIT or ORIENTATION_LANDSCAPE defined in
+     * {@link android.content.res.Configuration}.
+     */
+    @Orientation int mOrientation = 0;
+
+    /** The available space on device in bytes. */
+    @IntRange(from = 0) long mAvailableStorageBytes = 0;
+
+    /** Battery percentage. */
+    @IntRange(from = 0, to = 100) int mBatteryPercentage = 0;
+
+    /** The Service Provider Name (SPN) returned by {@link TelephonyManager#getSimOperatorName()} */
+    @NonNull String mCarrier = "";
+
+    /** @hide **/
+    @IntDef({
+            TelephonyManager.NETWORK_TYPE_UNKNOWN,
+            TelephonyManager.NETWORK_TYPE_GPRS,
+            TelephonyManager.NETWORK_TYPE_EDGE,
+            TelephonyManager.NETWORK_TYPE_UMTS,
+            TelephonyManager.NETWORK_TYPE_CDMA,
+            TelephonyManager.NETWORK_TYPE_EVDO_0,
+            TelephonyManager.NETWORK_TYPE_EVDO_A,
+            TelephonyManager.NETWORK_TYPE_1xRTT,
+            TelephonyManager.NETWORK_TYPE_HSDPA,
+            TelephonyManager.NETWORK_TYPE_HSUPA,
+            TelephonyManager.NETWORK_TYPE_HSPA,
+            TelephonyManager.NETWORK_TYPE_EVDO_B,
+            TelephonyManager.NETWORK_TYPE_LTE,
+            TelephonyManager.NETWORK_TYPE_EHRPD,
+            TelephonyManager.NETWORK_TYPE_HSPAP,
+            TelephonyManager.NETWORK_TYPE_GSM,
+            TelephonyManager.NETWORK_TYPE_TD_SCDMA,
+            TelephonyManager.NETWORK_TYPE_IWLAN,
+
+            //TODO: In order for @SystemApi methods to use this class, there cannot be any
+            // public hidden members.  This network type is marked as hidden because it is not a
+            // true network type and we are looking to remove it completely from the available list
+            // of network types.
+            //TelephonyManager.NETWORK_TYPE_LTE_CA,
+
+            TelephonyManager.NETWORK_TYPE_NR,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NetworkType {
+    }
+
+    /**
+     * A filtered subset of the Network capabilities of the device that contains upstream
+     * and downstream speeds, and whether the network is metered.
+     * This is an instance of {@link NetworkCapabilities} that contains the capability
+     * {@link android.net.NetworkCapabilities#NET_CAPABILITY_NOT_METERED} if the network is not
+     * metered, and {@link NetworkCapabilities#getLinkDownstreamBandwidthKbps()} and
+     * {@link NetworkCapabilities#getLinkUpstreamBandwidthKbps()} return the downstream and
+     * upstream connection speeds. All other methods of this {@link NetworkCapabilities} object
+     * return empty or default values.
+     */
+    @Nullable NetworkCapabilities mNetworkCapabilities = null;
+
+    /**
+     * Data network type. This is the value of
+     * {@link android.telephony.TelephonyManager#getDataNetworkType()}.
+     */
+    @NetworkType int mDataNetworkType = 0;
+
+    /** A map from package name to app information for installed and uninstalled apps. */
+    @DataClass.PluralOf("appInfo")
+    @NonNull Map<String, AppInfo> mAppInfos = Collections.emptyMap();
+
+    /** The device timezone +/- offset from UTC. */
+    @NonNull public Duration getTimezoneUtcOffset() {
+        return Duration.ofMinutes(mTimezoneUtcOffsetMins);
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/UserData.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ UserData(
+            int timezoneUtcOffsetMins,
+            @Orientation int orientation,
+            @IntRange(from = 0) long availableStorageBytes,
+            @IntRange(from = 0, to = 100) int batteryPercentage,
+            @NonNull String carrier,
+            @Nullable NetworkCapabilities networkCapabilities,
+            @NetworkType int dataNetworkType,
+            @NonNull Map<String,AppInfo> appInfos) {
+        this.mTimezoneUtcOffsetMins = timezoneUtcOffsetMins;
+        this.mOrientation = orientation;
+        AnnotationValidations.validate(
+                Orientation.class, null, mOrientation);
+        this.mAvailableStorageBytes = availableStorageBytes;
+        AnnotationValidations.validate(
+                IntRange.class, null, mAvailableStorageBytes,
+                "from", 0);
+        this.mBatteryPercentage = batteryPercentage;
+        AnnotationValidations.validate(
+                IntRange.class, null, mBatteryPercentage,
+                "from", 0,
+                "to", 100);
+        this.mCarrier = carrier;
+        AnnotationValidations.validate(
+                NonNull.class, null, mCarrier);
+        this.mNetworkCapabilities = networkCapabilities;
+        this.mDataNetworkType = dataNetworkType;
+        AnnotationValidations.validate(
+                NetworkType.class, null, mDataNetworkType);
+        this.mAppInfos = appInfos;
+        AnnotationValidations.validate(
+                NonNull.class, null, mAppInfos);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The device timezone +/- offset from UTC.
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public int getTimezoneUtcOffsetMins() {
+        return mTimezoneUtcOffsetMins;
+    }
+
+    /**
+     * The device orientation. The value can be one of the constants ORIENTATION_UNDEFINED,
+     * ORIENTATION_PORTRAIT or ORIENTATION_LANDSCAPE defined in
+     * {@link android.content.res.Configuration}.
+     */
+    @DataClass.Generated.Member
+    public @Orientation int getOrientation() {
+        return mOrientation;
+    }
+
+    /**
+     * The available space on device in bytes.
+     */
+    @DataClass.Generated.Member
+    public @IntRange(from = 0) long getAvailableStorageBytes() {
+        return mAvailableStorageBytes;
+    }
+
+    /**
+     * Battery percentage.
+     */
+    @DataClass.Generated.Member
+    public @IntRange(from = 0, to = 100) int getBatteryPercentage() {
+        return mBatteryPercentage;
+    }
+
+    /**
+     * The Service Provider Name (SPN) returned by {@link TelephonyManager#getSimOperatorName()}
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getCarrier() {
+        return mCarrier;
+    }
+
+    /**
+     * A filtered subset of the Network capabilities of the device that contains upstream
+     * and downstream speeds, and whether the network is metered.
+     * This is an instance of {@link NetworkCapabilities} that contains the capability
+     * {@link android.net.NetworkCapabilities#NET_CAPABILITY_NOT_METERED} if the network is not
+     * metered, and {@link NetworkCapabilities#getLinkDownstreamBandwidthKbps()} and
+     * {@link NetworkCapabilities#getLinkUpstreamBandwidthKbps()} return the downstream and
+     * upstream connection speeds. All other methods of this {@link NetworkCapabilities} object
+     * return empty or default values.
+     */
+    @DataClass.Generated.Member
+    public @Nullable NetworkCapabilities getNetworkCapabilities() {
+        return mNetworkCapabilities;
+    }
+
+    /**
+     * Data network type. This is the value of
+     * {@link android.telephony.TelephonyManager#getDataNetworkType()}.
+     */
+    @DataClass.Generated.Member
+    public @NetworkType int getDataNetworkType() {
+        return mDataNetworkType;
+    }
+
+    /**
+     * A map from package name to app information for installed and uninstalled apps.
+     */
+    @DataClass.Generated.Member
+    public @NonNull Map<String,AppInfo> getAppInfos() {
+        return mAppInfos;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(UserData other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        UserData that = (UserData) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && mTimezoneUtcOffsetMins == that.mTimezoneUtcOffsetMins
+                && mOrientation == that.mOrientation
+                && mAvailableStorageBytes == that.mAvailableStorageBytes
+                && mBatteryPercentage == that.mBatteryPercentage
+                && java.util.Objects.equals(mCarrier, that.mCarrier)
+                && java.util.Objects.equals(mNetworkCapabilities, that.mNetworkCapabilities)
+                && mDataNetworkType == that.mDataNetworkType
+                && java.util.Objects.equals(mAppInfos, that.mAppInfos);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + mTimezoneUtcOffsetMins;
+        _hash = 31 * _hash + mOrientation;
+        _hash = 31 * _hash + Long.hashCode(mAvailableStorageBytes);
+        _hash = 31 * _hash + mBatteryPercentage;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mCarrier);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mNetworkCapabilities);
+        _hash = 31 * _hash + mDataNetworkType;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mAppInfos);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        int flg = 0;
+        if (mNetworkCapabilities != null) flg |= 0x20;
+        dest.writeInt(flg);
+        dest.writeInt(mTimezoneUtcOffsetMins);
+        dest.writeInt(mOrientation);
+        dest.writeLong(mAvailableStorageBytes);
+        dest.writeInt(mBatteryPercentage);
+        dest.writeString(mCarrier);
+        if (mNetworkCapabilities != null) dest.writeTypedObject(mNetworkCapabilities, flags);
+        dest.writeInt(mDataNetworkType);
+        dest.writeMap(mAppInfos);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ UserData(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        int flg = in.readInt();
+        int timezoneUtcOffsetMins = in.readInt();
+        int orientation = in.readInt();
+        long availableStorageBytes = in.readLong();
+        int batteryPercentage = in.readInt();
+        String carrier = in.readString();
+        NetworkCapabilities networkCapabilities = (flg & 0x20) == 0 ? null : (NetworkCapabilities) in.readTypedObject(NetworkCapabilities.CREATOR);
+        int dataNetworkType = in.readInt();
+        Map<String,AppInfo> appInfos = new java.util.LinkedHashMap<>();
+        in.readMap(appInfos, AppInfo.class.getClassLoader());
+
+        this.mTimezoneUtcOffsetMins = timezoneUtcOffsetMins;
+        this.mOrientation = orientation;
+        AnnotationValidations.validate(
+                Orientation.class, null, mOrientation);
+        this.mAvailableStorageBytes = availableStorageBytes;
+        AnnotationValidations.validate(
+                IntRange.class, null, mAvailableStorageBytes,
+                "from", 0);
+        this.mBatteryPercentage = batteryPercentage;
+        AnnotationValidations.validate(
+                IntRange.class, null, mBatteryPercentage,
+                "from", 0,
+                "to", 100);
+        this.mCarrier = carrier;
+        AnnotationValidations.validate(
+                NonNull.class, null, mCarrier);
+        this.mNetworkCapabilities = networkCapabilities;
+        this.mDataNetworkType = dataNetworkType;
+        AnnotationValidations.validate(
+                NetworkType.class, null, mDataNetworkType);
+        this.mAppInfos = appInfos;
+        AnnotationValidations.validate(
+                NonNull.class, null, mAppInfos);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<UserData> CREATOR
+            = new Parcelable.Creator<UserData>() {
+        @Override
+        public UserData[] newArray(int size) {
+            return new UserData[size];
+        }
+
+        @Override
+        public UserData createFromParcel(@NonNull android.os.Parcel in) {
+            return new UserData(in);
+        }
+    };
+
+    /**
+     * A builder for {@link UserData}
+     * @hide
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private int mTimezoneUtcOffsetMins;
+        private @Orientation int mOrientation;
+        private @IntRange(from = 0) long mAvailableStorageBytes;
+        private @IntRange(from = 0, to = 100) int mBatteryPercentage;
+        private @NonNull String mCarrier;
+        private @Nullable NetworkCapabilities mNetworkCapabilities;
+        private @NetworkType int mDataNetworkType;
+        private @NonNull Map<String,AppInfo> mAppInfos;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * The device timezone +/- offset from UTC.
+         *
+         * @hide
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setTimezoneUtcOffsetMins(int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mTimezoneUtcOffsetMins = value;
+            return this;
+        }
+
+        /**
+         * The device orientation. The value can be one of the constants ORIENTATION_UNDEFINED,
+         * ORIENTATION_PORTRAIT or ORIENTATION_LANDSCAPE defined in
+         * {@link android.content.res.Configuration}.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setOrientation(@Orientation int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mOrientation = value;
+            return this;
+        }
+
+        /**
+         * The available space on device in bytes.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setAvailableStorageBytes(@IntRange(from = 0) long value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mAvailableStorageBytes = value;
+            return this;
+        }
+
+        /**
+         * Battery percentage.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setBatteryPercentage(@IntRange(from = 0, to = 100) int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mBatteryPercentage = value;
+            return this;
+        }
+
+        /**
+         * The Service Provider Name (SPN) returned by {@link TelephonyManager#getSimOperatorName()}
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setCarrier(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10;
+            mCarrier = value;
+            return this;
+        }
+
+        /**
+         * A filtered subset of the Network capabilities of the device that contains upstream
+         * and downstream speeds, and whether the network is metered.
+         * This is an instance of {@link NetworkCapabilities} that contains the capability
+         * {@link android.net.NetworkCapabilities#NET_CAPABILITY_NOT_METERED} if the network is not
+         * metered, and {@link NetworkCapabilities#getLinkDownstreamBandwidthKbps()} and
+         * {@link NetworkCapabilities#getLinkUpstreamBandwidthKbps()} return the downstream and
+         * upstream connection speeds. All other methods of this {@link NetworkCapabilities} object
+         * return empty or default values.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setNetworkCapabilities(@NonNull NetworkCapabilities value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x20;
+            mNetworkCapabilities = value;
+            return this;
+        }
+
+        /**
+         * Data network type. This is the value of
+         * {@link android.telephony.TelephonyManager#getDataNetworkType()}.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setDataNetworkType(@NetworkType int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x40;
+            mDataNetworkType = value;
+            return this;
+        }
+
+        /**
+         * A map from package name to app information for installed and uninstalled apps.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setAppInfos(@NonNull Map<String,AppInfo> value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x80;
+            mAppInfos = value;
+            return this;
+        }
+
+        /** @see #setAppInfos */
+        @DataClass.Generated.Member
+        public @NonNull Builder addAppInfo(@NonNull String key, @NonNull AppInfo value) {
+            if (mAppInfos == null) setAppInfos(new java.util.LinkedHashMap());
+            mAppInfos.put(key, value);
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull UserData build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x100; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mTimezoneUtcOffsetMins = 0;
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mOrientation = 0;
+            }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mAvailableStorageBytes = 0;
+            }
+            if ((mBuilderFieldsSet & 0x8) == 0) {
+                mBatteryPercentage = 0;
+            }
+            if ((mBuilderFieldsSet & 0x10) == 0) {
+                mCarrier = "";
+            }
+            if ((mBuilderFieldsSet & 0x20) == 0) {
+                mNetworkCapabilities = null;
+            }
+            if ((mBuilderFieldsSet & 0x40) == 0) {
+                mDataNetworkType = 0;
+            }
+            if ((mBuilderFieldsSet & 0x80) == 0) {
+                mAppInfos = Collections.emptyMap();
+            }
+            UserData o = new UserData(
+                    mTimezoneUtcOffsetMins,
+                    mOrientation,
+                    mAvailableStorageBytes,
+                    mBatteryPercentage,
+                    mCarrier,
+                    mNetworkCapabilities,
+                    mDataNetworkType,
+                    mAppInfos);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x100) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1707172832988L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/UserData.java",
+            inputSignatures = "  int mTimezoneUtcOffsetMins\n @android.adservices.ondevicepersonalization.UserData.Orientation int mOrientation\n @android.annotation.IntRange long mAvailableStorageBytes\n @android.annotation.IntRange int mBatteryPercentage\n @android.annotation.NonNull java.lang.String mCarrier\n @android.annotation.Nullable android.net.NetworkCapabilities mNetworkCapabilities\n @android.adservices.ondevicepersonalization.UserData.NetworkType int mDataNetworkType\n @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"appInfo\") @android.annotation.NonNull java.util.Map<java.lang.String,android.adservices.ondevicepersonalization.AppInfo> mAppInfos\npublic @android.annotation.NonNull java.time.Duration getTimezoneUtcOffset()\nclass UserData extends java.lang.Object implements [android.os.Parcelable]\[email protected](genHiddenBuilder=true, genEqualsHashCode=true, genConstDefs=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/WebTriggerInput.java b/android-35/android/adservices/ondevicepersonalization/WebTriggerInput.java
new file mode 100644
index 0000000..5c57cb6
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/WebTriggerInput.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.net.Uri;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * The input data for
+ * {@link IsolatedWorker#onWebTrigger(WebTriggerInput, android.os.OutcomeReceiver)}.
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = false, genHiddenConstructor = true, genEqualsHashCode = true)
+public final class WebTriggerInput {
+    /** The destination URL (landing page) where the trigger event occurred. */
+    @NonNull private Uri mDestinationUrl;
+
+    /** The app where the trigger event occurred */
+    @NonNull private String mAppPackageName;
+
+    /**
+     * Additional data returned by the server as part of the web trigger registration
+     * to be sent to the {@link IsolatedService}. This can be {@code null} if the server
+     * does not need to send data to the service for processing web triggers.
+     */
+    @NonNull private byte[] mData;
+
+    /** @hide */
+    public WebTriggerInput(@NonNull WebTriggerInputParcel parcel) {
+        this(parcel.getDestinationUrl(), parcel.getAppPackageName(), parcel.getData());
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/WebTriggerInput.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new WebTriggerInput.
+     *
+     * @param destinationUrl
+     *   The destination URL (landing page) where the trigger event occurred.
+     * @param appPackageName
+     *   The app where the trigger event occurred
+     * @param data
+     *   Additional data returned by the server as part of the web trigger registration
+     *   to be sent to the {@link IsolatedService}. This can be {@code null} if the server
+     *   does not need to send data to the service for processing web triggers.
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public WebTriggerInput(
+            @NonNull Uri destinationUrl,
+            @NonNull String appPackageName,
+            @NonNull byte[] data) {
+        this.mDestinationUrl = destinationUrl;
+        AnnotationValidations.validate(
+                NonNull.class, null, mDestinationUrl);
+        this.mAppPackageName = appPackageName;
+        AnnotationValidations.validate(
+                NonNull.class, null, mAppPackageName);
+        this.mData = data;
+        AnnotationValidations.validate(
+                NonNull.class, null, mData);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The destination URL (landing page) where the trigger event occurred.
+     */
+    @DataClass.Generated.Member
+    public @NonNull Uri getDestinationUrl() {
+        return mDestinationUrl;
+    }
+
+    /**
+     * The app where the trigger event occurred
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getAppPackageName() {
+        return mAppPackageName;
+    }
+
+    /**
+     * Additional data returned by the server as part of the web trigger registration
+     * to be sent to the {@link IsolatedService}. This can be {@code null} if the server
+     * does not need to send data to the service for processing web triggers.
+     */
+    @DataClass.Generated.Member
+    public @NonNull byte[] getData() {
+        return mData;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(WebTriggerInput other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        WebTriggerInput that = (WebTriggerInput) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mDestinationUrl, that.mDestinationUrl)
+                && java.util.Objects.equals(mAppPackageName, that.mAppPackageName)
+                && java.util.Arrays.equals(mData, that.mData);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mDestinationUrl);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mAppPackageName);
+        _hash = 31 * _hash + java.util.Arrays.hashCode(mData);
+        return _hash;
+    }
+
+    @DataClass.Generated(
+            time = 1707513068642L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/WebTriggerInput.java",
+            inputSignatures = "private @android.annotation.NonNull android.net.Uri mDestinationUrl\nprivate @android.annotation.NonNull java.lang.String mAppPackageName\nprivate @android.annotation.NonNull byte[] mData\nclass WebTriggerInput extends java.lang.Object implements []\[email protected](genBuilder=false, genHiddenConstructor=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/WebTriggerInputParcel.java b/android-35/android/adservices/ondevicepersonalization/WebTriggerInputParcel.java
new file mode 100644
index 0000000..71f44cc
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/WebTriggerInputParcel.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/**
+ * Parcelable version of {@link WebTriggerInput}.
+ * @hide
+ */
+@DataClass(genAidl = false, genHiddenBuilder = true)
+public final class WebTriggerInputParcel implements Parcelable {
+    /** The destination URL (landing page) where the trigger registration occurred. */
+    @NonNull private Uri mDestinationUrl;
+
+    /** The app where the trigger registration occurred */
+    @NonNull private String mAppPackageName;
+
+    /** The data to be sent to the isolated service.  */
+    @NonNull private byte[] mData;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/WebTriggerInputParcel.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ WebTriggerInputParcel(
+            @NonNull Uri destinationUrl,
+            @NonNull String appPackageName,
+            @NonNull byte[] data) {
+        this.mDestinationUrl = destinationUrl;
+        AnnotationValidations.validate(
+                NonNull.class, null, mDestinationUrl);
+        this.mAppPackageName = appPackageName;
+        AnnotationValidations.validate(
+                NonNull.class, null, mAppPackageName);
+        this.mData = data;
+        AnnotationValidations.validate(
+                NonNull.class, null, mData);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The destination URL (landing page) where the trigger registration occurred.
+     */
+    @DataClass.Generated.Member
+    public @NonNull Uri getDestinationUrl() {
+        return mDestinationUrl;
+    }
+
+    /**
+     * The app where the trigger registration occurred
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getAppPackageName() {
+        return mAppPackageName;
+    }
+
+    /**
+     * The data to be sent to the isolated service.
+     */
+    @DataClass.Generated.Member
+    public @NonNull byte[] getData() {
+        return mData;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeTypedObject(mDestinationUrl, flags);
+        dest.writeString(mAppPackageName);
+        dest.writeByteArray(mData);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ WebTriggerInputParcel(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        Uri destinationUrl = (Uri) in.readTypedObject(Uri.CREATOR);
+        String appPackageName = in.readString();
+        byte[] data = in.createByteArray();
+
+        this.mDestinationUrl = destinationUrl;
+        AnnotationValidations.validate(
+                NonNull.class, null, mDestinationUrl);
+        this.mAppPackageName = appPackageName;
+        AnnotationValidations.validate(
+                NonNull.class, null, mAppPackageName);
+        this.mData = data;
+        AnnotationValidations.validate(
+                NonNull.class, null, mData);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<WebTriggerInputParcel> CREATOR
+            = new Parcelable.Creator<WebTriggerInputParcel>() {
+        @Override
+        public WebTriggerInputParcel[] newArray(int size) {
+            return new WebTriggerInputParcel[size];
+        }
+
+        @Override
+        public WebTriggerInputParcel createFromParcel(@NonNull android.os.Parcel in) {
+            return new WebTriggerInputParcel(in);
+        }
+    };
+
+    /**
+     * A builder for {@link WebTriggerInputParcel}
+     * @hide
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @NonNull Uri mDestinationUrl;
+        private @NonNull String mAppPackageName;
+        private @NonNull byte[] mData;
+
+        private long mBuilderFieldsSet = 0L;
+
+        /**
+         * Creates a new Builder.
+         *
+         * @param destinationUrl
+         *   The destination URL (landing page) where the trigger registration occurred.
+         * @param appPackageName
+         *   The app where the trigger registration occurred
+         * @param data
+         *   The data to be sent to the isolated service.
+         */
+        public Builder(
+                @NonNull Uri destinationUrl,
+                @NonNull String appPackageName,
+                @NonNull byte[] data) {
+            mDestinationUrl = destinationUrl;
+            AnnotationValidations.validate(
+                    NonNull.class, null, mDestinationUrl);
+            mAppPackageName = appPackageName;
+            AnnotationValidations.validate(
+                    NonNull.class, null, mAppPackageName);
+            mData = data;
+            AnnotationValidations.validate(
+                    NonNull.class, null, mData);
+        }
+
+        /**
+         * The destination URL (landing page) where the trigger registration occurred.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setDestinationUrl(@NonNull Uri value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mDestinationUrl = value;
+            return this;
+        }
+
+        /**
+         * The app where the trigger registration occurred
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setAppPackageName(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mAppPackageName = value;
+            return this;
+        }
+
+        /**
+         * The data to be sent to the isolated service.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setData(@NonNull byte... value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mData = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull WebTriggerInputParcel build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8; // Mark builder used
+
+            WebTriggerInputParcel o = new WebTriggerInputParcel(
+                    mDestinationUrl,
+                    mAppPackageName,
+                    mData);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x8) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1707510196470L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/WebTriggerInputParcel.java",
+            inputSignatures = "private @android.annotation.NonNull android.net.Uri mDestinationUrl\nprivate @android.annotation.NonNull java.lang.String mAppPackageName\nprivate @android.annotation.NonNull byte[] mData\nclass WebTriggerInputParcel extends java.lang.Object implements [android.os.Parcelable]\[email protected](genAidl=false, genHiddenBuilder=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/WebTriggerOutput.java b/android-35/android/adservices/ondevicepersonalization/WebTriggerOutput.java
new file mode 100644
index 0000000..4dea0ab
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/WebTriggerOutput.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.adservices.ondevicepersonalization.flags.Flags;
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The result that should be returned by
+ * {@link IsolatedWorker#onWebTrigger(WebTriggerInput, android.os.OutcomeReceiver)}.
+ * This class contains data that should be written to the REQUESTS or EVENTS tables.
+ * The contents of these tables can be consumed by Federated Learning facilitated model training,
+ * or Federated Analytics facilitated cross-device statistical analysis.
+ */
+@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
+@DataClass(genBuilder = true, genEqualsHashCode = true)
+public final class WebTriggerOutput {
+    /**
+     * Persistent data to be written to the REQUESTS table after
+     * {@link IsolatedWorker#onWebTrigger(WebTriggerInput, android.os.OutcomeReceiver)}
+     * completes. This can be {@code null} if no data needs to be written to
+     * the REQUESTS table.
+     */
+    @DataClass.MaySetToNull
+    @Nullable private RequestLogRecord mRequestLogRecord = null;
+
+    /**
+     * A list of {@link EventLogRecord} objects to be written to the EVENTS table. Each
+     * {@link EventLogRecord} must be associated with an existing {@link RequestLogRecord} in
+     * the REQUESTS table, specified using
+     * {@link EventLogRecord.Builder#setRequestLogRecord(RequestLogRecord)}.
+     * If the {@link RequestLogRecord} is not specified, the {@link EventLogRecord} will not be
+     * written. The list can be empty if no data needs to be written to the EVENTS table.
+     */
+    @DataClass.PluralOf("eventLogRecord")
+    @NonNull private List<EventLogRecord> mEventLogRecords = Collections.emptyList();
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/WebTriggerOutput.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ WebTriggerOutput(
+            @Nullable RequestLogRecord requestLogRecord,
+            @NonNull List<EventLogRecord> eventLogRecords) {
+        this.mRequestLogRecord = requestLogRecord;
+        this.mEventLogRecords = eventLogRecords;
+        AnnotationValidations.validate(
+                NonNull.class, null, mEventLogRecords);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Persistent data to be written to the REQUESTS table after
+     * {@link IsolatedWorker#onWebTrigger(WebTriggerInput, android.os.OutcomeReceiver)}
+     * completes. This can be {@code null} if no data needs to be written to
+     * the REQUESTS table.
+     */
+    @DataClass.Generated.Member
+    public @Nullable RequestLogRecord getRequestLogRecord() {
+        return mRequestLogRecord;
+    }
+
+    /**
+     * A list of {@link EventLogRecord} objects to be written to the EVENTS table. Each
+     * {@link EventLogRecord} must be associated with an existing {@link RequestLogRecord} in
+     * the REQUESTS table, specified using
+     * {@link EventLogRecord.Builder#setRequestLogRecord(RequestLogRecord)}.
+     * If the {@link RequestLogRecord} is not specified, the {@link EventLogRecord} will not be
+     * written. The list can be empty if no data needs to be written to the EVENTS table.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<EventLogRecord> getEventLogRecords() {
+        return mEventLogRecords;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(WebTriggerOutput other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        WebTriggerOutput that = (WebTriggerOutput) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mRequestLogRecord, that.mRequestLogRecord)
+                && java.util.Objects.equals(mEventLogRecords, that.mEventLogRecords);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mRequestLogRecord);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mEventLogRecords);
+        return _hash;
+    }
+
+    /**
+     * A builder for {@link WebTriggerOutput}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @Nullable RequestLogRecord mRequestLogRecord;
+        private @NonNull List<EventLogRecord> mEventLogRecords;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * Persistent data to be written to the REQUESTS table after
+         * {@link IsolatedWorker#onWebTrigger(WebTriggerInput, android.os.OutcomeReceiver)}
+         * completes. This can be {@code null} if no data needs to be written to
+         * the REQUESTS table.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setRequestLogRecord(@Nullable RequestLogRecord value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mRequestLogRecord = value;
+            return this;
+        }
+
+        /**
+         * A list of {@link EventLogRecord} objects to be written to the EVENTS table. Each
+         * {@link EventLogRecord} must be associated with an existing {@link RequestLogRecord} in
+         * the REQUESTS table, specified using
+         * {@link EventLogRecord.Builder#setRequestLogRecord(RequestLogRecord)}.
+         * If the {@link RequestLogRecord} is not specified, the {@link EventLogRecord} will not be
+         * written. The list can be empty if no data needs to be written to the EVENTS table.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setEventLogRecords(@NonNull List<EventLogRecord> value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mEventLogRecords = value;
+            return this;
+        }
+
+        /** @see #setEventLogRecords */
+        @DataClass.Generated.Member
+        public @NonNull Builder addEventLogRecord(@NonNull EventLogRecord value) {
+            if (mEventLogRecords == null) setEventLogRecords(new java.util.ArrayList<>());
+            mEventLogRecords.add(value);
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull WebTriggerOutput build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mRequestLogRecord = null;
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mEventLogRecords = Collections.emptyList();
+            }
+            WebTriggerOutput o = new WebTriggerOutput(
+                    mRequestLogRecord,
+                    mEventLogRecords);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x4) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1707251898683L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/WebTriggerOutput.java",
+            inputSignatures = "private @com.android.ondevicepersonalization.internal.util.DataClass.MaySetToNull @android.annotation.Nullable android.adservices.ondevicepersonalization.RequestLogRecord mRequestLogRecord\nprivate @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"eventLogRecord\") @android.annotation.NonNull java.util.List<android.adservices.ondevicepersonalization.EventLogRecord> mEventLogRecords\nclass WebTriggerOutput extends java.lang.Object implements []\[email protected](genBuilder=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/ondevicepersonalization/WebTriggerOutputParcel.java b/android-35/android/adservices/ondevicepersonalization/WebTriggerOutputParcel.java
new file mode 100644
index 0000000..0671a31
--- /dev/null
+++ b/android-35/android/adservices/ondevicepersonalization/WebTriggerOutputParcel.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.ondevicepersonalization;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Parcelable version of {@link WebTriggerOutput}.
+ * @hide
+ */
+@DataClass(genAidl = false, genBuilder = false)
+public final class WebTriggerOutputParcel implements Parcelable {
+    /**
+     * Persistent data to be written to the REQUESTS table after
+     * {@link IsolatedWorker#onWebTrigger(WebTriggerInput, android.os.OutcomeReceiver)}
+     * completes. If null, no persistent data will be written.
+     */
+    @Nullable private RequestLogRecord mRequestLogRecord = null;
+
+    /**
+     * A list of {@link EventLogRecord}. Writes events to the EVENTS table and associates
+     * them with requests with the specified corresponding {@link RequestLogRecord} from
+     * {@link EventLogRecord#getRequestLogRecord()}.
+     * If the event does not contain a {@link RequestLogRecord} that was previously written
+     * by this service, the {@link EventLogRecord} is not written.
+     *
+     */
+    @DataClass.PluralOf("eventLogRecord")
+    @NonNull private List<EventLogRecord> mEventLogRecords = Collections.emptyList();
+
+    /** @hide */
+    public WebTriggerOutputParcel(@NonNull WebTriggerOutput value) {
+        this(value.getRequestLogRecord(), value.getEventLogRecords());
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/WebTriggerOutputParcel.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new WebTriggerOutputParcel.
+     *
+     * @param requestLogRecord
+     *   Persistent data to be written to the REQUESTS table after
+     *   {@link IsolatedWorker#onWebTrigger(WebTriggerInput, android.os.OutcomeReceiver)}
+     *   completes. If null, no persistent data will be written.
+     * @param eventLogRecords
+     *   A list of {@link EventLogRecord}. Writes events to the EVENTS table and associates
+     *   them with requests with the specified corresponding {@link RequestLogRecord} from
+     *   {@link EventLogRecord#getRequestLogRecord()}.
+     *   If the event does not contain a {@link RequestLogRecord} that was previously written
+     *   by this service, the {@link EventLogRecord} is not written.
+     */
+    @DataClass.Generated.Member
+    public WebTriggerOutputParcel(
+            @Nullable RequestLogRecord requestLogRecord,
+            @NonNull List<EventLogRecord> eventLogRecords) {
+        this.mRequestLogRecord = requestLogRecord;
+        this.mEventLogRecords = eventLogRecords;
+        AnnotationValidations.validate(
+                NonNull.class, null, mEventLogRecords);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Persistent data to be written to the REQUESTS table after
+     * {@link IsolatedWorker#onWebTrigger(WebTriggerInput, android.os.OutcomeReceiver)}
+     * completes. If null, no persistent data will be written.
+     */
+    @DataClass.Generated.Member
+    public @Nullable RequestLogRecord getRequestLogRecord() {
+        return mRequestLogRecord;
+    }
+
+    /**
+     * A list of {@link EventLogRecord}. Writes events to the EVENTS table and associates
+     * them with requests with the specified corresponding {@link RequestLogRecord} from
+     * {@link EventLogRecord#getRequestLogRecord()}.
+     * If the event does not contain a {@link RequestLogRecord} that was previously written
+     * by this service, the {@link EventLogRecord} is not written.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<EventLogRecord> getEventLogRecords() {
+        return mEventLogRecords;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mRequestLogRecord != null) flg |= 0x1;
+        dest.writeByte(flg);
+        if (mRequestLogRecord != null) dest.writeTypedObject(mRequestLogRecord, flags);
+        dest.writeParcelableList(mEventLogRecords, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ WebTriggerOutputParcel(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        RequestLogRecord requestLogRecord = (flg & 0x1) == 0 ? null : (RequestLogRecord) in.readTypedObject(RequestLogRecord.CREATOR);
+        List<EventLogRecord> eventLogRecords = new java.util.ArrayList<>();
+        in.readParcelableList(eventLogRecords, EventLogRecord.class.getClassLoader());
+
+        this.mRequestLogRecord = requestLogRecord;
+        this.mEventLogRecords = eventLogRecords;
+        AnnotationValidations.validate(
+                NonNull.class, null, mEventLogRecords);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<WebTriggerOutputParcel> CREATOR
+            = new Parcelable.Creator<WebTriggerOutputParcel>() {
+        @Override
+        public WebTriggerOutputParcel[] newArray(int size) {
+            return new WebTriggerOutputParcel[size];
+        }
+
+        @Override
+        public WebTriggerOutputParcel createFromParcel(@NonNull android.os.Parcel in) {
+            return new WebTriggerOutputParcel(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1704482141383L,
+            codegenVersion = "1.0.23",
+            sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/WebTriggerOutputParcel.java",
+            inputSignatures = "private @android.annotation.Nullable android.adservices.ondevicepersonalization.RequestLogRecord mRequestLogRecord\nprivate @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"eventLogRecord\") @android.annotation.NonNull java.util.List<android.adservices.ondevicepersonalization.EventLogRecord> mEventLogRecords\nclass WebTriggerOutputParcel extends java.lang.Object implements [android.os.Parcelable]\[email protected](genAidl=false, genBuilder=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/android-35/android/adservices/shell/ShellCommandParam.java b/android-35/android/adservices/shell/ShellCommandParam.java
new file mode 100644
index 0000000..2b35410
--- /dev/null
+++ b/android-35/android/adservices/shell/ShellCommandParam.java
@@ -0,0 +1,88 @@
+/*
+ * 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 android.adservices.shell;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Represents request which contains command and args as an input to runShellCommand API.
+ *
+ * @hide
+ */
+public class ShellCommandParam implements Parcelable {
+
+    /* Array containing command name with all the args */
+    private final String[] mCommandArgs;
+
+    public ShellCommandParam(String... commandArgs) {
+        mCommandArgs = Objects.requireNonNull(commandArgs);
+    }
+
+    private ShellCommandParam(Parcel in) {
+        this(in.createStringArray());
+    }
+
+    public static final Creator<ShellCommandParam> CREATOR =
+            new Parcelable.Creator<>() {
+                @Override
+                public ShellCommandParam createFromParcel(Parcel in) {
+                    return new ShellCommandParam(in);
+                }
+
+                @Override
+                public ShellCommandParam[] newArray(int size) {
+                    return new ShellCommandParam[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeStringArray(mCommandArgs);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof ShellCommandParam)) {
+            return false;
+        }
+
+        ShellCommandParam that = (ShellCommandParam) o;
+        return Arrays.equals(mCommandArgs, that.mCommandArgs);
+    }
+
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(mCommandArgs);
+    }
+
+    /** Get the command name with all the args as a list. */
+    public String[] getCommandArgs() {
+        return mCommandArgs;
+    }
+}
diff --git a/android-35/android/adservices/shell/ShellCommandResult.java b/android-35/android/adservices/shell/ShellCommandResult.java
new file mode 100644
index 0000000..442c67e
--- /dev/null
+++ b/android-35/android/adservices/shell/ShellCommandResult.java
@@ -0,0 +1,136 @@
+/*
+ * 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 android.adservices.shell;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Represents the response from the runShellCommand API.
+ *
+ * @hide
+ */
+public class ShellCommandResult implements Parcelable {
+
+    private static final int RESULT_OK = 0;
+
+    private final int mResultCode;
+    @Nullable private final String mOut;
+    @Nullable private final String mErr;
+
+    private ShellCommandResult(int resultCode, @Nullable String out, @Nullable String err) {
+        mResultCode = resultCode;
+        mOut = out;
+        mErr = err;
+    }
+
+    private ShellCommandResult(Parcel in) {
+        this(in.readInt(), in.readString(), in.readString());
+    }
+
+    private ShellCommandResult(Builder builder) {
+        this(builder.mResultCode, builder.mOut, builder.mErr);
+    }
+
+    public static final Creator<ShellCommandResult> CREATOR =
+            new Parcelable.Creator<>() {
+                @Override
+                public ShellCommandResult createFromParcel(Parcel in) {
+                    Objects.requireNonNull(in);
+                    return new ShellCommandResult(in);
+                }
+
+                @Override
+                public ShellCommandResult[] newArray(int size) {
+                    return new ShellCommandResult[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+        dest.writeInt(mResultCode);
+        dest.writeString(mOut);
+        dest.writeString(mErr);
+    }
+
+    /** Returns the command status. */
+    public int getResultCode() {
+        return mResultCode;
+    }
+
+    /** Returns {@code true} if {@link #getResultCode} is greater than equal to 0. */
+    public boolean isSuccess() {
+        return getResultCode() >= 0;
+    }
+
+    /** Returns the output of the shell command result. */
+    @Nullable
+    public String getOut() {
+        return mOut;
+    }
+
+    /** Returns the error message associated with this response. */
+    @Nullable
+    public String getErr() {
+        return mErr;
+    }
+
+    /**
+     * Builder for {@link ShellCommandResult}.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private int mResultCode = RESULT_OK;
+        @Nullable private String mOut;
+        @Nullable private String mErr;
+
+        public Builder() {}
+
+        /** Sets the Status Code. */
+        public Builder setResultCode(int resultCode) {
+            mResultCode = resultCode;
+            return this;
+        }
+
+        /** Sets the shell command output in case of success. */
+        public Builder setOut(@Nullable String out) {
+            mOut = out;
+            return this;
+        }
+
+        /** Sets the error message in case of command failure. */
+        public Builder setErr(@Nullable String err) {
+            mErr = err;
+            return this;
+        }
+
+        /** Builds a {@link ShellCommandResult} object. */
+        public ShellCommandResult build() {
+            return new ShellCommandResult(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/signals/ProtectedSignalsManager.java b/android-35/android/adservices/signals/ProtectedSignalsManager.java
new file mode 100644
index 0000000..566d3da
--- /dev/null
+++ b/android-35/android/adservices/signals/ProtectedSignalsManager.java
@@ -0,0 +1,233 @@
+/*
+ * 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 android.adservices.signals;
+
+import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_PROTECTED_SIGNALS;
+
+import android.adservices.common.AdServicesStatusUtils;
+import android.adservices.common.FledgeErrorResponse;
+import android.adservices.common.SandboxedSdkContextUtils;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.RequiresApi;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.app.sdksandbox.SandboxedSdkContext;
+import android.content.Context;
+import android.os.Build;
+import android.os.LimitExceededException;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+
+import com.android.adservices.AdServicesCommon;
+import com.android.adservices.LoggerFactory;
+import com.android.adservices.ServiceBinder;
+import com.android.adservices.flags.Flags;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/** ProtectedSignalsManager provides APIs for apps and ad-SDKs to manage their protected signals. */
+@FlaggedApi(Flags.FLAG_PROTECTED_SIGNALS_ENABLED)
+@RequiresApi(Build.VERSION_CODES.S)
+public class ProtectedSignalsManager {
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger();
+    /**
+     * Constant that represents the service name for {@link ProtectedSignalsManager} to be used in
+     * {@link android.adservices.AdServicesFrameworkInitializer#registerServiceWrappers}
+     *
+     * @hide
+     */
+    public static final String PROTECTED_SIGNALS_SERVICE = "protected_signals_service";
+
+    @NonNull private Context mContext;
+    @NonNull private ServiceBinder<IProtectedSignalsService> mServiceBinder;
+
+    /**
+     * Factory method for creating an instance of ProtectedSignalsManager.
+     *
+     * @param context The {@link Context} to use
+     * @return A {@link ProtectedSignalsManager} instance
+     */
+    @SuppressLint("ManagerLookup")
+    @NonNull
+    // TODO(b/303896680): Investigate why this lint was not triggered for similar managers
+    public static ProtectedSignalsManager get(@NonNull Context context) {
+        // On T+, context.getSystemService() does more than just call constructor.
+        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+                ? context.getSystemService(ProtectedSignalsManager.class)
+                : new ProtectedSignalsManager(context);
+    }
+
+    /**
+     * Create a service binder ProtectedSignalsManager
+     *
+     * @hide
+     */
+    public ProtectedSignalsManager(@NonNull Context context) {
+        Objects.requireNonNull(context);
+
+        // In case the ProtectedSignalsManager is initiated from inside a sdk_sandbox process the
+        // fields will be immediately rewritten by the initialize method below.
+        initialize(context);
+    }
+
+    /**
+     * Initializes {@link ProtectedSignalsManager} with the given {@code context}.
+     *
+     * <p>This method is called by the {@link SandboxedSdkContext} to propagate the correct context.
+     * For more information check the javadoc on the {@link
+     * android.app.sdksandbox.SdkSandboxSystemServiceRegistry}.
+     *
+     * @hide
+     * @see android.app.sdksandbox.SdkSandboxSystemServiceRegistry
+     */
+    public ProtectedSignalsManager initialize(@NonNull Context context) {
+        Objects.requireNonNull(context);
+
+        mContext = context;
+        mServiceBinder =
+                ServiceBinder.getServiceBinder(
+                        context,
+                        AdServicesCommon.ACTION_PROTECTED_SIGNALS_SERVICE,
+                        IProtectedSignalsService.Stub::asInterface);
+        return this;
+    }
+
+    @NonNull
+    IProtectedSignalsService getService() {
+        IProtectedSignalsService service = mServiceBinder.getService();
+        if (service == null) {
+            throw new IllegalStateException("Unable to find the service");
+        }
+        return service;
+    }
+
+    /**
+     * The updateSignals API will retrieve a JSON from the URI that describes which signals to add
+     * or remove. This API also allows registering the encoder endpoint. The endpoint is used to
+     * download an encoding logic, which enables encoding the signals.
+     *
+     * <p>The top level keys for the JSON must correspond to one of 5 commands:
+     *
+     * <p>"put" - Adds a new signal, overwriting any existing signals with the same key. The value
+     * for this is a JSON object where the keys are base 64 strings corresponding to the key to put
+     * for and the values are base 64 string corresponding to the value to put.
+     *
+     * <p>"append" - Appends a new signal/signals to a time series of signals, removing the oldest
+     * signals to make room for the new ones if the size of the series exceeds the given maximum.
+     * The value for this is a JSON object where the keys are base 64 strings corresponding to the
+     * key to append to and the values are objects with two fields: "values" and "maxSignals" .
+     * "values" is a list of base 64 strings corresponding to signal values to append to the time
+     * series. "maxSignals" is the maximum number of values that are allowed in this timeseries. If
+     * the current number of signals associated with the key exceeds maxSignals the oldest signals
+     * will be removed. Note that you can append to a key added by put. Not that appending more than
+     * the maximum number of values will cause a failure.
+     *
+     * <p>"put_if_not_present" - Adds a new signal only if there are no existing signals with the
+     * same key. The value for this is a JSON object where the keys are base 64 strings
+     * corresponding to the key to put for and the values are base 64 string corresponding to the
+     * value to put.
+     *
+     * <p>"remove" - Removes the signal for a key. The value of this is a list of base 64 strings
+     * corresponding to the keys of signals that should be deleted.
+     *
+     * <p>"update_encoder" - Provides an action to update the endpoint, and a URI which can be used
+     * to retrieve an encoding logic. The sub-key for providing an update action is "action" and the
+     * values currently supported are:
+     *
+     * <ol>
+     *   <li>"REGISTER" : Registers the encoder endpoint if provided for the first time or
+     *       overwrites the existing one with the newly provided endpoint. Providing the "endpoint"
+     *       is required for the "REGISTER" action.
+     * </ol>
+     *
+     * <p>The sub-key for providing an encoder endpoint is "endpoint" and the value is the URI
+     * string for the endpoint.
+     *
+     * <p>On success, the onResult method of the provided OutcomeReceiver will be called with an
+     * empty Object. This Object has no significance and is used merely as a placeholder.
+     *
+     * <p>Key may only be operated on by one command per JSON. If two command attempt to operate on
+     * the same key, this method will through an {@link IllegalArgumentException}
+     *
+     * <p>This call fails with an {@link SecurityException} if
+     *
+     * <ol>
+     *   <li>the {@code ownerPackageName} is not calling app's package name and/or
+     *   <li>the buyer is not authorized to use the API.
+     * </ol>
+     *
+     * <p>This call fails with an {@link IllegalArgumentException} if
+     *
+     * <ol>
+     *   <li>The JSON retrieved from the server is not valid.
+     *   <li>The provided URI is invalid.
+     * </ol>
+     *
+     * <p>This call fails with {@link LimitExceededException} if the calling package exceeds the
+     * allowed rate limits and is throttled.
+     *
+     * <p>This call fails with an {@link IllegalStateException} if an internal service error is
+     * encountered.
+     */
+    @RequiresPermission(ACCESS_ADSERVICES_PROTECTED_SIGNALS)
+    public void updateSignals(
+            @NonNull UpdateSignalsRequest updateSignalsRequest,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Object, Exception> receiver) {
+        Objects.requireNonNull(updateSignalsRequest);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(receiver);
+
+        try {
+            final IProtectedSignalsService service = getService();
+
+            service.updateSignals(
+                    new UpdateSignalsInput.Builder(
+                                    updateSignalsRequest.getUpdateUri(), getCallerPackageName())
+                            .build(),
+                    new UpdateSignalsCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            executor.execute(() -> receiver.onResult(new Object()));
+                        }
+
+                        @Override
+                        public void onFailure(FledgeErrorResponse failureParcel) {
+                            executor.execute(
+                                    () ->
+                                            receiver.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            failureParcel)));
+                        }
+                    });
+        } catch (RemoteException e) {
+            sLogger.e(e, "Exception");
+            receiver.onError(new IllegalStateException("Internal Error!", e));
+        }
+    }
+
+    private String getCallerPackageName() {
+        SandboxedSdkContext sandboxedSdkContext =
+                SandboxedSdkContextUtils.getAsSandboxedSdkContext(mContext);
+        return sandboxedSdkContext == null
+                ? mContext.getPackageName()
+                : sandboxedSdkContext.getClientPackageName();
+    }
+}
diff --git a/android-35/android/adservices/signals/UpdateSignalsInput.java b/android-35/android/adservices/signals/UpdateSignalsInput.java
new file mode 100644
index 0000000..26a909f
--- /dev/null
+++ b/android-35/android/adservices/signals/UpdateSignalsInput.java
@@ -0,0 +1,189 @@
+/*
+ * 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 android.adservices.signals;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * The input object wrapping the parameters for the updateSignals API.
+ *
+ * <p>Refer to {@link UpdateSignalsRequest} for more information about the parameters.
+ *
+ * @hide
+ */
+public final class UpdateSignalsInput implements Parcelable {
+    @NonNull private final Uri mUpdateUri;
+    @NonNull private final String mCallerPackageName;
+
+    @NonNull
+    public static final Creator<UpdateSignalsInput> CREATOR =
+            new Creator<>() {
+                @NonNull
+                @Override
+                public UpdateSignalsInput createFromParcel(@NonNull Parcel in) {
+                    return new UpdateSignalsInput(in);
+                }
+
+                @NonNull
+                @Override
+                public UpdateSignalsInput[] newArray(int size) {
+                    return new UpdateSignalsInput[size];
+                }
+            };
+
+    private UpdateSignalsInput(@NonNull Uri updateUri, @NonNull String callerPackageName) {
+        Objects.requireNonNull(updateUri);
+        Objects.requireNonNull(callerPackageName);
+
+        mUpdateUri = updateUri;
+        mCallerPackageName = callerPackageName;
+    }
+
+    private UpdateSignalsInput(@NonNull Parcel in) {
+        Objects.requireNonNull(in);
+
+        Uri updateUri = Uri.CREATOR.createFromParcel(in);
+        Objects.requireNonNull(updateUri);
+        mUpdateUri = updateUri;
+        String callerPackageName = in.readString();
+        Objects.requireNonNull(callerPackageName);
+        mCallerPackageName = callerPackageName;
+    }
+
+    /**
+     * @return the {@link Uri} from which the signal updates will be fetched.
+     */
+    @NonNull
+    public Uri getUpdateUri() {
+        return mUpdateUri;
+    }
+
+    /**
+     * @return the caller app's package name.
+     */
+    @NonNull
+    public String getCallerPackageName() {
+        return mCallerPackageName;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Objects.requireNonNull(dest);
+
+        mUpdateUri.writeToParcel(dest, flags);
+        dest.writeString(mCallerPackageName);
+    }
+
+    /**
+     * @return {@code true} if and only if the other object is {@link UpdateSignalsRequest} with the
+     *     same update URI and package name
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof UpdateSignalsInput)) return false;
+        UpdateSignalsInput that = (UpdateSignalsInput) o;
+        return mUpdateUri.equals(that.mUpdateUri)
+                && mCallerPackageName.equals(that.mCallerPackageName);
+    }
+
+    /**
+     * @return the hash of the {@link UpdateSignalsInput} object's data.
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUpdateUri, mCallerPackageName);
+    }
+
+    @Override
+    public String toString() {
+        return "UpdateSignalsInput{"
+                + "mUpdateUri="
+                + mUpdateUri
+                + ", mCallerPackageName='"
+                + mCallerPackageName
+                + '\''
+                + '}';
+    }
+
+    /**
+     * Builder for {@link UpdateSignalsInput} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        @NonNull private Uri mUpdateUri;
+        @NonNull private String mCallerPackageName;
+
+        /**
+         * Instantiates a {@link UpdateSignalsInput.Builder} with the {@link Uri} from which the
+         * JSON is to be fetched and the caller app's package name.
+         *
+         * @throws NullPointerException if any non-null parameter is null.
+         */
+        public Builder(@NonNull Uri updateUri, @NonNull String callerPackageName) {
+            Objects.requireNonNull(updateUri);
+            Objects.requireNonNull(callerPackageName);
+
+            this.mUpdateUri = updateUri;
+            this.mCallerPackageName = callerPackageName;
+        }
+
+        /**
+         * Sets the {@link Uri} from which the signal updates will be fetched.
+         *
+         * <p>See {@link #getUpdateUri()} ()} for details.
+         */
+        @NonNull
+        public Builder setUpdateUri(@NonNull Uri updateUri) {
+            Objects.requireNonNull(updateUri);
+            this.mUpdateUri = updateUri;
+            return this;
+        }
+
+        /**
+         * Sets the caller app's package name.
+         *
+         * <p>See {@link #getCallerPackageName()} for details.
+         */
+        @NonNull
+        public Builder setCallerPackageName(@NonNull String callerPackageName) {
+            Objects.requireNonNull(callerPackageName);
+            this.mCallerPackageName = callerPackageName;
+            return this;
+        }
+
+        /**
+         * Builds an instance of a {@link UpdateSignalsInput}.
+         *
+         * @throws NullPointerException if any non-null parameter is null.
+         */
+        @NonNull
+        public UpdateSignalsInput build() {
+            return new UpdateSignalsInput(mUpdateUri, mCallerPackageName);
+        }
+    }
+}
diff --git a/android-35/android/adservices/signals/UpdateSignalsRequest.java b/android-35/android/adservices/signals/UpdateSignalsRequest.java
new file mode 100644
index 0000000..717d913
--- /dev/null
+++ b/android-35/android/adservices/signals/UpdateSignalsRequest.java
@@ -0,0 +1,113 @@
+/*
+ * 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 android.adservices.signals;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.net.Uri;
+
+import com.android.adservices.flags.Flags;
+
+import java.util.Objects;
+
+/**
+ * The request object for updateSignals.
+ *
+ * <p>{@code updateUri} is the only parameter. It represents the URI that the service will reach out
+ * to retrieve the signals updates.
+ */
+@FlaggedApi(Flags.FLAG_PROTECTED_SIGNALS_ENABLED)
+public final class UpdateSignalsRequest {
+    @NonNull private final Uri mUpdateUri;
+
+    private UpdateSignalsRequest(@NonNull Uri updateUri) {
+        Objects.requireNonNull(updateUri, "updateUri must not be null in UpdateSignalsRequest");
+
+        mUpdateUri = updateUri;
+    }
+
+    /**
+     * @return the {@link Uri} from which the signal updates will be fetched.
+     */
+    @NonNull
+    public Uri getUpdateUri() {
+        return mUpdateUri;
+    }
+
+    /**
+     * @return {@code true} if and only if the other object is {@link UpdateSignalsRequest} with the
+     *     same update URI.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof UpdateSignalsRequest)) return false;
+        UpdateSignalsRequest other = (UpdateSignalsRequest) o;
+        return mUpdateUri.equals(other.mUpdateUri);
+    }
+
+    /**
+     * @return the hash of the {@link UpdateSignalsRequest} object's data.
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUpdateUri);
+    }
+
+    /**
+     * @return a human-readable representation of {@link UpdateSignalsRequest}.
+     */
+    @Override
+    public String toString() {
+        return "UpdateSignalsRequest{" + "updateUri=" + mUpdateUri + '}';
+    }
+
+    /** Builder for {@link UpdateSignalsRequest} objects. */
+    public static final class Builder {
+        @NonNull private Uri mUpdateUri;
+
+        /**
+         * Instantiates a {@link Builder} with the {@link Uri} from which the signal updates will be
+         * fetched.
+         */
+        public Builder(@NonNull Uri updateUri) {
+            Objects.requireNonNull(updateUri);
+            this.mUpdateUri = updateUri;
+        }
+
+        /**
+         * Sets the {@link Uri} from which the JSON is to be fetched.
+         *
+         * <p>See {@link #getUpdateUri()} ()} for details.
+         */
+        @NonNull
+        public Builder setUpdateUri(@NonNull Uri updateUri) {
+            Objects.requireNonNull(updateUri, "updateUri must not be null in UpdateSignalsRequest");
+            this.mUpdateUri = updateUri;
+            return this;
+        }
+
+        /**
+         * Builds an instance of a {@link UpdateSignalsRequest}.
+         *
+         * @throws NullPointerException if any non-null parameter is null.
+         */
+        @NonNull
+        public UpdateSignalsRequest build() {
+            return new UpdateSignalsRequest(mUpdateUri);
+        }
+    }
+}
diff --git a/android-35/android/adservices/topics/EncryptedTopic.java b/android-35/android/adservices/topics/EncryptedTopic.java
new file mode 100644
index 0000000..3892c70
--- /dev/null
+++ b/android-35/android/adservices/topics/EncryptedTopic.java
@@ -0,0 +1,111 @@
+/*
+ * 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 android.adservices.topics;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+
+import com.android.adservices.flags.Flags;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Encrypted form of {@link android.adservices.topics.Topic}. This object will be used to return
+ * encrypted topic cipher text along with necessary fields required to decrypt it.
+ *
+ * <p>Decryption of {@link EncryptedTopic#getEncryptedTopic()} should give json string for {@link
+ * Topic}. Example of decrypted json string: {@code { "taxonomy_version": 5, "model_version": 2,
+ * "topic_id": 10010 }}
+ *
+ * <p>Decryption of cipher text is expected to happen on the server with the corresponding algorithm
+ * and private key for the public key {@link EncryptedTopic#getKeyIdentifier()}}.
+ *
+ * <p>Detailed steps on decryption can be found on <a
+ * href="https://developer.android.com/design-for-safety/privacy-sandbox/guides/topics">Developer
+ * Guide</a>.
+ */
+@FlaggedApi(Flags.FLAG_TOPICS_ENCRYPTION_ENABLED)
+public final class EncryptedTopic {
+    @NonNull private final byte[] mEncryptedTopic;
+    @NonNull private final String mKeyIdentifier;
+    @NonNull private final byte[] mEncapsulatedKey;
+
+    /**
+     * Creates encrypted version of the {@link Topic} object.
+     *
+     * @param encryptedTopic byte array cipher text containing encrypted {@link Topic} json string.
+     * @param keyIdentifier key used to identify the public key used for encryption.
+     * @param encapsulatedKey encapsulated key generated during HPKE setup.
+     */
+    public EncryptedTopic(
+            @NonNull byte[] encryptedTopic,
+            @NonNull String keyIdentifier,
+            @NonNull byte[] encapsulatedKey) {
+        mEncryptedTopic = Objects.requireNonNull(encryptedTopic);
+        mKeyIdentifier = Objects.requireNonNull(keyIdentifier);
+        mEncapsulatedKey = Objects.requireNonNull(encapsulatedKey);
+    }
+
+    /** Returns encrypted bytes for the JSON version of the {@link Topic} object as cipher text. */
+    @NonNull
+    public byte[] getEncryptedTopic() {
+        return mEncryptedTopic;
+    }
+
+    /** Returns key identifier for the used encryption key. */
+    @NonNull
+    public String getKeyIdentifier() {
+        return mKeyIdentifier;
+    }
+
+    /** Returns the encapsulated key generated during HPKE setup. */
+    @NonNull
+    public byte[] getEncapsulatedKey() {
+        return mEncapsulatedKey;
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) return true;
+        if (!(object instanceof EncryptedTopic)) return false;
+        EncryptedTopic encryptedTopic = (EncryptedTopic) object;
+        return Arrays.equals(getEncryptedTopic(), encryptedTopic.getEncryptedTopic())
+                && getKeyIdentifier().equals(encryptedTopic.getKeyIdentifier())
+                && Arrays.equals(getEncapsulatedKey(), encryptedTopic.getEncapsulatedKey());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                Arrays.hashCode(getEncryptedTopic()),
+                getKeyIdentifier(),
+                Arrays.hashCode(getEncapsulatedKey()));
+    }
+
+    @Override
+    public java.lang.String toString() {
+        return "EncryptedTopic{"
+                + "mEncryptedTopic="
+                + Arrays.toString(mEncryptedTopic)
+                + ", mKeyIdentifier="
+                + mKeyIdentifier
+                + ", mEncapsulatedKey="
+                + Arrays.toString(mEncapsulatedKey)
+                + '}';
+    }
+}
diff --git a/android-35/android/adservices/topics/GetTopicsParam.java b/android-35/android/adservices/topics/GetTopicsParam.java
new file mode 100644
index 0000000..ce80436
--- /dev/null
+++ b/android-35/android/adservices/topics/GetTopicsParam.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.topics;
+
+import static android.adservices.topics.TopicsManager.EMPTY_SDK;
+import static android.adservices.topics.TopicsManager.RECORD_OBSERVATION_DEFAULT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represent input params to the getTopics API.
+ *
+ * @hide
+ */
+public final class GetTopicsParam implements Parcelable {
+    private final String mSdkName;
+    private final String mSdkPackageName;
+    private final String mAppPackageName;
+    private final boolean mRecordObservation;
+
+    private GetTopicsParam(
+            @NonNull String sdkName,
+            @Nullable String sdkPackageName,
+            @NonNull String appPackageName,
+            boolean recordObservation) {
+        mSdkName = sdkName;
+        mSdkPackageName = sdkPackageName;
+        mAppPackageName = appPackageName;
+        mRecordObservation = recordObservation;
+    }
+
+    private GetTopicsParam(@NonNull Parcel in) {
+        mSdkName = in.readString();
+        mSdkPackageName = in.readString();
+        mAppPackageName = in.readString();
+        mRecordObservation = in.readBoolean();
+    }
+
+    public static final @NonNull Creator<GetTopicsParam> CREATOR =
+            new Parcelable.Creator<GetTopicsParam>() {
+                @Override
+                public GetTopicsParam createFromParcel(Parcel in) {
+                    return new GetTopicsParam(in);
+                }
+
+                @Override
+                public GetTopicsParam[] newArray(int size) {
+                    return new GetTopicsParam[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeString(mSdkName);
+        out.writeString(mSdkPackageName);
+        out.writeString(mAppPackageName);
+        out.writeBoolean(mRecordObservation);
+    }
+
+    /** Get the Sdk Name. This is the name in the <sdk-library> tag of the Manifest. */
+    @NonNull
+    public String getSdkName() {
+        return mSdkName;
+    }
+
+    /** Get the Sdk Package Name. This is the package name in the Manifest. */
+    @NonNull
+    public String getSdkPackageName() {
+        return mSdkPackageName;
+    }
+
+    /** Get the App PackageName. */
+    @NonNull
+    public String getAppPackageName() {
+        return mAppPackageName;
+    }
+
+    /** Get the Record Observation. */
+    public boolean shouldRecordObservation() {
+        return mRecordObservation;
+    }
+
+    /** Builder for {@link GetTopicsParam} objects. */
+    public static final class Builder {
+        private String mSdkName;
+        private String mSdkPackageName;
+        private String mAppPackageName;
+        private boolean mRecordObservation = RECORD_OBSERVATION_DEFAULT;
+
+        public Builder() {}
+
+        /**
+         * Set the Sdk Name. When the app calls the Topics API directly without using a SDK, don't
+         * set this field.
+         */
+        public @NonNull Builder setSdkName(@NonNull String sdkName) {
+            mSdkName = sdkName;
+            return this;
+        }
+
+        /**
+         * Set the Sdk Package Name. When the app calls the Topics API directly without using an
+         * SDK, don't set this field.
+         */
+        public @NonNull Builder setSdkPackageName(@NonNull String sdkPackageName) {
+            mSdkPackageName = sdkPackageName;
+            return this;
+        }
+
+        /** Set the App PackageName. */
+        public @NonNull Builder setAppPackageName(@NonNull String appPackageName) {
+            mAppPackageName = appPackageName;
+            return this;
+        }
+
+        /**
+         * Set the Record Observation. Whether to record that the caller has observed the topics of
+         * the host app or not. This will be used to determine if the caller can receive the topic
+         * in the next epoch.
+         */
+        public @NonNull Builder setShouldRecordObservation(boolean recordObservation) {
+            mRecordObservation = recordObservation;
+            return this;
+        }
+
+        /** Builds a {@link GetTopicsParam} instance. */
+        public @NonNull GetTopicsParam build() {
+            if (mSdkName == null) {
+                // When Sdk name is not set, we assume the App calls the Topics API directly.
+                // We set the Sdk name to empty to mark this.
+                mSdkName = EMPTY_SDK;
+            }
+
+            if (mSdkPackageName == null) {
+                // When Sdk package name is not set, we assume the App calls the Topics API
+                // directly.
+                // We set the Sdk package name to empty to mark this.
+                mSdkPackageName = EMPTY_SDK;
+            }
+
+            if (mAppPackageName == null || mAppPackageName.isEmpty()) {
+                throw new IllegalArgumentException("App PackageName must not be empty or null");
+            }
+
+            return new GetTopicsParam(
+                    mSdkName, mSdkPackageName, mAppPackageName, mRecordObservation);
+        }
+    }
+}
diff --git a/android-35/android/adservices/topics/GetTopicsRequest.java b/android-35/android/adservices/topics/GetTopicsRequest.java
new file mode 100644
index 0000000..cc8e51b
--- /dev/null
+++ b/android-35/android/adservices/topics/GetTopicsRequest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.topics;
+
+import static android.adservices.topics.TopicsManager.EMPTY_SDK;
+import static android.adservices.topics.TopicsManager.RECORD_OBSERVATION_DEFAULT;
+
+import android.annotation.NonNull;
+
+/** Get Topics Request. */
+public final class GetTopicsRequest {
+
+    /** Name of Ads SDK that is involved in this request. */
+    private final String mAdsSdkName;
+
+    /** Whether to record that the caller has observed the topics of the host app or not. */
+    private final boolean mRecordObservation;
+
+    private GetTopicsRequest(@NonNull Builder builder) {
+        mAdsSdkName = builder.mAdsSdkName;
+        mRecordObservation = builder.mRecordObservation;
+    }
+
+    /** Get the Sdk Name. */
+    @NonNull
+    public String getAdsSdkName() {
+        return mAdsSdkName;
+    }
+
+    /** Get Record Observation. */
+    public boolean shouldRecordObservation() {
+        return mRecordObservation;
+    }
+
+    /** Builder for {@link GetTopicsRequest} objects. */
+    public static final class Builder {
+        private String mAdsSdkName = EMPTY_SDK;
+        private boolean mRecordObservation = RECORD_OBSERVATION_DEFAULT;
+
+        /** Creates a {@link Builder} for {@link GetTopicsRequest} objects. */
+        public Builder() {}
+
+        /**
+         * Set Ads Sdk Name.
+         *
+         * <p>This must be called by SDKs running outside of the Sandbox. Other clients must not
+         * call it.
+         *
+         * @param adsSdkName the Ads Sdk Name.
+         */
+        @NonNull
+        public Builder setAdsSdkName(@NonNull String adsSdkName) {
+            // This is the case the SDK calling from outside of the Sandbox.
+            // Check if the caller set the adsSdkName
+            if (adsSdkName == null) {
+                throw new IllegalArgumentException(
+                        "When calling Topics API outside of the Sandbox, caller should set Ads Sdk"
+                                + " Name");
+            }
+
+            mAdsSdkName = adsSdkName;
+            return this;
+        }
+
+        /**
+         * Set the Record Observation.
+         *
+         * @param recordObservation whether to record that the caller has observed the topics of the
+         *     host app or not. This will be used to determine if the caller can receive the topic
+         *     in the next epoch.
+         */
+        @NonNull
+        public Builder setShouldRecordObservation(boolean recordObservation) {
+            mRecordObservation = recordObservation;
+            return this;
+        }
+
+        /** Builds a {@link GetTopicsRequest} instance. */
+        @NonNull
+        public GetTopicsRequest build() {
+            return new GetTopicsRequest(this);
+        }
+    }
+}
diff --git a/android-35/android/adservices/topics/GetTopicsResponse.java b/android-35/android/adservices/topics/GetTopicsResponse.java
new file mode 100644
index 0000000..45e8de4
--- /dev/null
+++ b/android-35/android/adservices/topics/GetTopicsResponse.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.topics;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+
+import com.android.adservices.flags.Flags;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/** Represent the result from the getTopics API. */
+public final class GetTopicsResponse {
+    /** List of Topic objects returned by getTopics API. */
+    private final List<Topic> mTopics;
+
+    /** List of EncryptedTopic objects returned by getTopics API. */
+    private final List<EncryptedTopic> mEncryptedTopics;
+
+    private GetTopicsResponse(List<Topic> topics, List<EncryptedTopic> encryptedTopics) {
+        mTopics = topics;
+        mEncryptedTopics = encryptedTopics;
+    }
+
+    /** Returns a {@link List} of {@link Topic} objects returned by getTopics API. */
+    @NonNull
+    public List<Topic> getTopics() {
+        return mTopics;
+    }
+
+    /** Returns a {@link List} of {@link EncryptedTopic} objects returned by getTopics API. */
+    @NonNull
+    @FlaggedApi(Flags.FLAG_TOPICS_ENCRYPTION_ENABLED)
+    public List<EncryptedTopic> getEncryptedTopics() {
+        return mEncryptedTopics;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof GetTopicsResponse)) {
+            return false;
+        }
+        GetTopicsResponse that = (GetTopicsResponse) o;
+        return mTopics.equals(that.mTopics) && mEncryptedTopics.equals(that.mEncryptedTopics);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mTopics, mEncryptedTopics);
+    }
+
+    /**
+     * Builder for {@link GetTopicsResponse} objects. This class should be used in test
+     * implementation as expected response from Topics API
+     */
+    public static final class Builder {
+        private List<Topic> mTopics = new ArrayList<>();
+        private List<EncryptedTopic> mEncryptedTopics = new ArrayList<>();
+
+        /**
+         * Creates a {@link Builder} for {@link GetTopicsResponse} objects.
+         *
+         * @param topics The list of the returned Topics.
+         * @deprecated This function is deprecated.
+         */
+        @Deprecated
+        public Builder(@NonNull List<Topic> topics) {
+            mTopics = Objects.requireNonNull(topics);
+        }
+
+        /**
+         * Creates a {@link Builder} for {@link GetTopicsResponse} objects.
+         *
+         * @param topics The list of the returned Topics.
+         * @param encryptedTopics The list of encrypted Topics.
+         */
+        @FlaggedApi(Flags.FLAG_TOPICS_ENCRYPTION_ENABLED)
+        public Builder(@NonNull List<Topic> topics, @NonNull List<EncryptedTopic> encryptedTopics) {
+            mTopics = Objects.requireNonNull(topics);
+            mEncryptedTopics = Objects.requireNonNull(encryptedTopics);
+        }
+
+        /**
+         * Builds a {@link GetTopicsResponse} instance.
+         *
+         * @throws IllegalArgumentException if any of the params are null.
+         */
+        @NonNull
+        public GetTopicsResponse build() {
+            if (mTopics == null || mEncryptedTopics == null) {
+                throw new IllegalArgumentException("Topics is null");
+            }
+            return new GetTopicsResponse(mTopics, mEncryptedTopics);
+        }
+    }
+}
diff --git a/android-35/android/adservices/topics/GetTopicsResult.java b/android-35/android/adservices/topics/GetTopicsResult.java
new file mode 100644
index 0000000..0cc654b
--- /dev/null
+++ b/android-35/android/adservices/topics/GetTopicsResult.java
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.topics;
+
+import static android.adservices.common.AdServicesStatusUtils.STATUS_SUCCESS;
+
+import android.adservices.common.AdServicesResponse;
+import android.adservices.common.AdServicesStatusUtils;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represent the result from the getTopics API.
+ *
+ * @hide
+ */
+public final class GetTopicsResult extends AdServicesResponse {
+    private final List<Long> mTaxonomyVersions;
+    private final List<Long> mModelVersions;
+    private final List<Integer> mTopics;
+    private final List<byte[]> mEncryptedTopics;
+    private final List<String> mEncryptionKeys;
+    private final List<byte[]> mEncapsulatedKeys;
+
+    private GetTopicsResult(
+            @AdServicesStatusUtils.StatusCode int resultCode,
+            String errorMessage,
+            List<Long> taxonomyVersions,
+            List<Long> modelVersions,
+            List<Integer> topics,
+            List<byte[]> encryptedTopics,
+            List<String> encryptionKeys,
+            List<byte[]> encapsulatedKeys) {
+        super(resultCode, errorMessage);
+        mTaxonomyVersions = taxonomyVersions;
+        mModelVersions = modelVersions;
+        mTopics = topics;
+        mEncryptedTopics = encryptedTopics;
+        mEncryptionKeys = encryptionKeys;
+        mEncapsulatedKeys = encapsulatedKeys;
+    }
+
+    private GetTopicsResult(@NonNull Parcel in) {
+        super(in.readInt(), in.readString());
+
+        mTaxonomyVersions = Collections.unmodifiableList(readLongList(in));
+        mModelVersions = Collections.unmodifiableList(readLongList(in));
+        mTopics = Collections.unmodifiableList(readIntegerList(in));
+        mEncryptedTopics = Collections.unmodifiableList(readByteArrayList(in));
+        mEncryptionKeys = Collections.unmodifiableList(readStringList(in));
+        mEncapsulatedKeys = Collections.unmodifiableList(readByteArrayList(in));
+    }
+
+    public static final @NonNull Creator<GetTopicsResult> CREATOR =
+            new Parcelable.Creator<GetTopicsResult>() {
+                @Override
+                public GetTopicsResult createFromParcel(Parcel in) {
+                    return new GetTopicsResult(in);
+                }
+
+                @Override
+                public GetTopicsResult[] newArray(int size) {
+                    return new GetTopicsResult[size];
+                }
+            };
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mStatusCode);
+        out.writeString(mErrorMessage);
+        writeLongList(out, mTaxonomyVersions);
+        writeLongList(out, mModelVersions);
+        writeIntegerList(out, mTopics);
+        writeByteArrayList(out, mEncryptedTopics);
+        writeStringList(out, mEncryptionKeys);
+        writeByteArrayList(out, mEncapsulatedKeys);
+    }
+
+    /**
+     * Returns {@code true} if {@link #getResultCode} equals {@link
+     * AdServicesStatusUtils#STATUS_SUCCESS}.
+     */
+    public boolean isSuccess() {
+        return getResultCode() == STATUS_SUCCESS;
+    }
+
+    /** Returns one of the {@code RESULT} constants defined in {@link GetTopicsResult}. */
+    public @AdServicesStatusUtils.StatusCode int getResultCode() {
+        return mStatusCode;
+    }
+
+    /**
+     * Returns the error message associated with this result.
+     *
+     * <p>If {@link #isSuccess} is {@code true}, the error message is always {@code null}. The error
+     * message may be {@code null} even if {@link #isSuccess} is {@code false}.
+     */
+    @Nullable
+    public String getErrorMessage() {
+        return mErrorMessage;
+    }
+
+    /** Get the Taxonomy Versions. */
+    public List<Long> getTaxonomyVersions() {
+        return mTaxonomyVersions;
+    }
+
+    /** Get the Model Versions. */
+    public List<Long> getModelVersions() {
+        return mModelVersions;
+    }
+
+    @NonNull
+    public List<Integer> getTopics() {
+        return mTopics;
+    }
+
+    @NonNull
+    public List<byte[]> getEncryptedTopics() {
+        return mEncryptedTopics;
+    }
+
+    @NonNull
+    public List<String> getEncryptionKeys() {
+        return mEncryptionKeys;
+    }
+
+    @NonNull
+    public List<byte[]> getEncapsulatedKeys() {
+        return mEncapsulatedKeys;
+    }
+
+    @Override
+    public String toString() {
+        return "GetTopicsResult{"
+                + "mResultCode="
+                + mStatusCode
+                + ", mErrorMessage='"
+                + mErrorMessage
+                + '\''
+                + ", mTaxonomyVersions="
+                + mTaxonomyVersions
+                + ", mModelVersions="
+                + mModelVersions
+                + ", mTopics="
+                + mTopics
+                + ", mEncryptedTopics="
+                + prettyPrint(mEncryptedTopics)
+                + ", mEncryptionKeys="
+                + mEncryptionKeys
+                + ", mEncapsulatedKeys="
+                + prettyPrint(mEncapsulatedKeys)
+                + '}';
+    }
+
+    private String prettyPrint(List<byte[]> listOfByteArrays) {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("[");
+        for (int index = 0; index < listOfByteArrays.size(); index++) {
+            stringBuilder.append(Arrays.toString(listOfByteArrays.get(index)));
+            if (index != listOfByteArrays.size() - 1) {
+                stringBuilder.append(", ");
+            }
+        }
+        stringBuilder.append("]");
+        return stringBuilder.toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof GetTopicsResult)) {
+            return false;
+        }
+
+        GetTopicsResult that = (GetTopicsResult) o;
+
+        return mStatusCode == that.mStatusCode
+                && Objects.equals(mErrorMessage, that.mErrorMessage)
+                && mTaxonomyVersions.equals(that.mTaxonomyVersions)
+                && mModelVersions.equals(that.mModelVersions)
+                && mTopics.equals(that.mTopics)
+                && equals(mEncryptedTopics, that.mEncryptedTopics)
+                && mEncryptionKeys.equals(that.mEncryptionKeys);
+    }
+
+    private static boolean equals(List<byte[]> list1, List<byte[]> list2) {
+        if (list1 == null || list2 == null) {
+            return false;
+        }
+        if (list1.size() != list2.size()) {
+            return false;
+        }
+        for (int i = 0; i < list1.size(); i++) {
+            if (!Arrays.equals(list1.get(i), list2.get(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mStatusCode,
+                mErrorMessage,
+                mTaxonomyVersions,
+                mModelVersions,
+                mTopics,
+                hashCode(mEncryptedTopics),
+                mEncryptionKeys,
+                hashCode(mEncryptedTopics));
+    }
+
+    private static int hashCode(List<byte[]> list) {
+        int hash = 0;
+        for (byte[] bytes : list) {
+            hash += Arrays.hashCode(bytes);
+        }
+        return hash;
+    }
+
+    // Read the list of long from parcel.
+    private static List<Long> readLongList(@NonNull Parcel in) {
+        List<Long> list = new ArrayList<>();
+
+        int toReadCount = in.readInt();
+        // Negative toReadCount is handled implicitly
+        for (int i = 0; i < toReadCount; i++) {
+            list.add(in.readLong());
+        }
+
+        return list;
+    }
+
+    // Read the list of integer from parcel.
+    private static List<Integer> readIntegerList(@NonNull Parcel in) {
+        List<Integer> list = new ArrayList<>();
+
+        int toReadCount = in.readInt();
+        // Negative toReadCount is handled implicitly
+        for (int i = 0; i < toReadCount; i++) {
+            list.add(in.readInt());
+        }
+
+        return list;
+    }
+
+    // Read the list of integer from parcel.
+    private static List<String> readStringList(@NonNull Parcel in) {
+        List<String> list = new ArrayList<>();
+
+        int toReadCount = in.readInt();
+        // Negative toReadCount is handled implicitly
+        for (int i = 0; i < toReadCount; i++) {
+            list.add(in.readString());
+        }
+
+        return list;
+    }
+
+    // Read the list of byte arrays from parcel.
+    private static List<byte[]> readByteArrayList(@NonNull Parcel in) {
+        List<byte[]> list = new ArrayList<>();
+
+        int toReadCount = in.readInt();
+        // Negative toReadCount is handled implicitly
+        for (int i = 0; i < toReadCount; i++) {
+            list.add(in.createByteArray());
+        }
+
+        return list;
+    }
+
+    // Write a List of Long to parcel.
+    private static void writeLongList(@NonNull Parcel out, @Nullable List<Long> val) {
+        if (val == null) {
+            out.writeInt(-1);
+            return;
+        }
+        out.writeInt(val.size());
+        for (Long l : val) {
+            out.writeLong(l);
+        }
+    }
+
+    // Write a List of Integer to parcel.
+    private static void writeIntegerList(@NonNull Parcel out, @Nullable List<Integer> val) {
+        if (val == null) {
+            out.writeInt(-1);
+            return;
+        }
+        out.writeInt(val.size());
+        for (Integer integer : val) {
+            out.writeInt(integer);
+        }
+    }
+
+    // Write a List of String to parcel.
+    private static void writeStringList(@NonNull Parcel out, @Nullable List<String> val) {
+        if (val == null) {
+            out.writeInt(-1);
+            return;
+        }
+        out.writeInt(val.size());
+        for (String string : val) {
+            out.writeString(string);
+        }
+    }
+
+    // Write a List of byte array to parcel.
+    private static void writeByteArrayList(@NonNull Parcel out, @Nullable List<byte[]> val) {
+        if (val == null) {
+            out.writeInt(-1);
+            return;
+        }
+        out.writeInt(val.size());
+        for (byte[] bytes : val) {
+            out.writeByteArray(bytes);
+        }
+    }
+
+    /**
+     * Builder for {@link GetTopicsResult} objects.
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private @AdServicesStatusUtils.StatusCode int mResultCode;
+        private String mErrorMessage;
+        private List<Long> mTaxonomyVersions = new ArrayList<>();
+        private List<Long> mModelVersions = new ArrayList<>();
+        private List<Integer> mTopics = new ArrayList<>();
+        private List<byte[]> mEncryptedTopics = new ArrayList<>();
+        private List<String> mEncryptionKeys = new ArrayList<>();
+        private List<byte[]> mEncapsulatedKeys = new ArrayList<>();
+
+        public Builder() {}
+
+        /** Set the Result Code. */
+        @NonNull
+        public Builder setResultCode(@AdServicesStatusUtils.StatusCode int resultCode) {
+            mResultCode = resultCode;
+            return this;
+        }
+
+        /** Set the Error Message. */
+        @NonNull
+        public Builder setErrorMessage(@Nullable String errorMessage) {
+            mErrorMessage = errorMessage;
+            return this;
+        }
+
+        /** Set the Taxonomy Version. */
+        @NonNull
+        public Builder setTaxonomyVersions(@NonNull List<Long> taxonomyVersions) {
+            mTaxonomyVersions = taxonomyVersions;
+            return this;
+        }
+
+        /** Set the Model Version. */
+        @NonNull
+        public Builder setModelVersions(@NonNull List<Long> modelVersions) {
+            mModelVersions = modelVersions;
+            return this;
+        }
+
+        /** Set the list of the returned Topics */
+        @NonNull
+        public Builder setTopics(@NonNull List<Integer> topics) {
+            mTopics = topics;
+            return this;
+        }
+
+        /** Set the list of the returned encrypted topics */
+        @NonNull
+        public Builder setEncryptedTopics(@NonNull List<byte[]> encryptedTopics) {
+            mEncryptedTopics = encryptedTopics;
+            return this;
+        }
+
+        /** Set the list of the encryption keys */
+        @NonNull
+        public Builder setEncryptionKeys(@NonNull List<String> encryptionKeys) {
+            mEncryptionKeys = encryptionKeys;
+            return this;
+        }
+
+        /** Set the list of encapsulated keys generated via encryption */
+        @NonNull
+        public Builder setEncapsulatedKeys(@NonNull List<byte[]> encapsulatedKeys) {
+            mEncapsulatedKeys = encapsulatedKeys;
+            return this;
+        }
+
+        /**
+         * Builds a {@link GetTopicsResult} instance.
+         *
+         * <p>throws IllegalArgumentException if any of the params are null or there is any mismatch
+         * in the size of lists.
+         */
+        @NonNull
+        public GetTopicsResult build() {
+            if (mTopics == null
+                    || mTaxonomyVersions == null
+                    || mModelVersions == null
+                    || mEncryptedTopics == null
+                    || mEncryptionKeys == null) {
+                throw new IllegalArgumentException(
+                        "One of the mandatory params of GetTopicsResult is null");
+            }
+
+            if (mTopics.size() != mTaxonomyVersions.size()
+                    || mTopics.size() != mModelVersions.size()) {
+                throw new IllegalArgumentException("Size mismatch in Topics");
+            }
+
+            if (mEncryptedTopics.size() != mEncryptionKeys.size()
+                    || mEncryptedTopics.size() != mEncapsulatedKeys.size()) {
+                throw new IllegalArgumentException("Size mismatch in EncryptedTopic lists");
+            }
+
+            return new GetTopicsResult(
+                    mResultCode,
+                    mErrorMessage,
+                    mTaxonomyVersions,
+                    mModelVersions,
+                    mTopics,
+                    mEncryptedTopics,
+                    mEncryptionKeys,
+                    mEncapsulatedKeys);
+        }
+    }
+}
diff --git a/android-35/android/adservices/topics/Topic.java b/android-35/android/adservices/topics/Topic.java
new file mode 100644
index 0000000..593762a
--- /dev/null
+++ b/android-35/android/adservices/topics/Topic.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.topics;
+
+import java.util.Objects;
+
+/** Represent the topic result from the getTopics API. */
+public final class Topic {
+    private final long mTaxonomyVersion;
+    private final long mModelVersion;
+    private final int mTopicId;
+
+    /**
+     * Creates an object which represents the result from the getTopics API.
+     *
+     * @param mTaxonomyVersion a long representing the version of the taxonomy.
+     * @param mModelVersion a long representing the version of the model.
+     * @param mTopicId an integer representing the unique id of a topic.
+     */
+    public Topic(long mTaxonomyVersion, long mModelVersion, int mTopicId) {
+        this.mTaxonomyVersion = mTaxonomyVersion;
+        this.mModelVersion = mModelVersion;
+        this.mTopicId = mTopicId;
+    }
+
+    /** Get the ModelVersion. */
+    public long getModelVersion() {
+        return mModelVersion;
+    }
+
+    /** Get the TaxonomyVersion. */
+    public long getTaxonomyVersion() {
+        return mTaxonomyVersion;
+    }
+
+    /** Get the Topic ID. */
+    public int getTopicId() {
+        return mTopicId;
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) return true;
+        if (!(object instanceof Topic)) return false;
+        Topic topic = (Topic) object;
+        return getTaxonomyVersion() == topic.getTaxonomyVersion()
+                && getModelVersion() == topic.getModelVersion()
+                && getTopicId() == topic.getTopicId();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getTaxonomyVersion(), getModelVersion(), getTopicId());
+    }
+
+    @Override
+    public java.lang.String toString() {
+        return "Topic{"
+                + "mTaxonomyVersion="
+                + mTaxonomyVersion
+                + ", mModelVersion="
+                + mModelVersion
+                + ", mTopicCode="
+                + mTopicId
+                + '}';
+    }
+}
diff --git a/android-35/android/adservices/topics/TopicsManager.java b/android-35/android/adservices/topics/TopicsManager.java
new file mode 100644
index 0000000..b1c8dd6
--- /dev/null
+++ b/android-35/android/adservices/topics/TopicsManager.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2022 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 android.adservices.topics;
+
+import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS;
+
+import android.adservices.common.AdServicesStatusUtils;
+import android.adservices.common.CallerMetadata;
+import android.adservices.common.SandboxedSdkContextUtils;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
+import android.app.sdksandbox.SandboxedSdkContext;
+import android.content.Context;
+import android.os.Build;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.text.TextUtils;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.adservices.AdServicesCommon;
+import com.android.adservices.LoggerFactory;
+import com.android.adservices.ServiceBinder;
+import com.android.adservices.shared.common.ServiceUnavailableException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * TopicsManager provides APIs for App and Ad-Sdks to get the user interest topics in a privacy
+ * preserving way.
+ *
+ * <p>The instance of the {@link TopicsManager} can be obtained using {@link
+ * Context#getSystemService} and {@link TopicsManager} class.
+ */
+@RequiresApi(Build.VERSION_CODES.S)
+public final class TopicsManager {
+    private static final LoggerFactory.Logger sLogger = LoggerFactory.getTopicsLogger();
+    /**
+     * Constant that represents the service name for {@link TopicsManager} to be used in {@link
+     * android.adservices.AdServicesFrameworkInitializer#registerServiceWrappers}
+     *
+     * @hide
+     */
+    public static final String TOPICS_SERVICE = "topics_service";
+
+    // When an app calls the Topics API directly, it sets the SDK name to empty string.
+    static final String EMPTY_SDK = "";
+
+    // Default value is true to record SDK's Observation when it calls Topics API.
+    static final boolean RECORD_OBSERVATION_DEFAULT = true;
+
+    private Context mContext;
+    private ServiceBinder<ITopicsService> mServiceBinder;
+
+    /**
+     * Factory method for creating an instance of TopicsManager.
+     *
+     * @param context The {@link Context} to use
+     * @return A {@link TopicsManager} instance
+     */
+    @NonNull
+    public static TopicsManager get(@NonNull Context context) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
+            throw new ServiceUnavailableException();
+        }
+        // On TM+, context.getSystemService() does more than just call constructor.
+        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+                ? context.getSystemService(TopicsManager.class)
+                : new TopicsManager(context);
+    }
+
+    /**
+     * Create TopicsManager
+     *
+     * @hide
+     */
+    public TopicsManager(Context context) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
+            throw new ServiceUnavailableException();
+        }
+        // In case the TopicsManager is initiated from inside a sdk_sandbox process the fields
+        // will be immediately rewritten by the initialize method below.
+        initialize(context);
+    }
+
+    /**
+     * Initializes {@link TopicsManager} with the given {@code context}.
+     *
+     * <p>This method is called by the {@link SandboxedSdkContext} to propagate the correct context.
+     * For more information check the javadoc on the {@link
+     * android.app.sdksandbox.SdkSandboxSystemServiceRegistry}.
+     *
+     * @hide
+     * @see android.app.sdksandbox.SdkSandboxSystemServiceRegistry
+     */
+    public TopicsManager initialize(Context context) {
+        mContext = context;
+        mServiceBinder =
+                ServiceBinder.getServiceBinder(
+                        context,
+                        AdServicesCommon.ACTION_TOPICS_SERVICE,
+                        ITopicsService.Stub::asInterface);
+        return this;
+    }
+
+    @NonNull
+    private ITopicsService getService() {
+        ITopicsService service = mServiceBinder.getService();
+        if (service == null) {
+            throw new ServiceUnavailableException();
+        }
+        return service;
+    }
+
+    /**
+     * Return the topics.
+     *
+     * @param getTopicsRequest The request for obtaining Topics.
+     * @param executor The executor to run callback.
+     * @param callback The callback that's called after topics are available or an error occurs.
+     * @throws IllegalStateException if this API is not available.
+     */
+    @NonNull
+    @RequiresPermission(ACCESS_ADSERVICES_TOPICS)
+    public void getTopics(
+            @NonNull GetTopicsRequest getTopicsRequest,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<GetTopicsResponse, Exception> callback) {
+        Objects.requireNonNull(getTopicsRequest);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+        CallerMetadata callerMetadata =
+                new CallerMetadata.Builder()
+                        .setBinderElapsedTimestamp(SystemClock.elapsedRealtime())
+                        .build();
+        final ITopicsService service = getService();
+        String sdkName = getTopicsRequest.getAdsSdkName();
+        String appPackageName = "";
+        String sdkPackageName = "";
+        // First check if context is SandboxedSdkContext or not
+        SandboxedSdkContext sandboxedSdkContext =
+                SandboxedSdkContextUtils.getAsSandboxedSdkContext(mContext);
+        if (sandboxedSdkContext != null) {
+            // This is the case with the Sandbox.
+            sdkPackageName = sandboxedSdkContext.getSdkPackageName();
+            appPackageName = sandboxedSdkContext.getClientPackageName();
+
+            if (!TextUtils.isEmpty(sdkName)) {
+                throw new IllegalArgumentException(
+                        "When calling Topics API from Sandbox, caller should not set Ads Sdk Name");
+            }
+
+            String sdkNameFromSandboxedContext = sandboxedSdkContext.getSdkName();
+            if (null == sdkNameFromSandboxedContext || sdkNameFromSandboxedContext.isEmpty()) {
+                throw new IllegalArgumentException(
+                        "Sdk Name From SandboxedSdkContext should not be null or empty");
+            }
+
+            sdkName = sdkNameFromSandboxedContext;
+        } else {
+            // This is the case without the Sandbox.
+            if (null == sdkName) {
+                // When adsSdkName is not set, we assume the App calls the Topics API directly.
+                // We set the adsSdkName to empty to mark this.
+                sdkName = EMPTY_SDK;
+            }
+            appPackageName = mContext.getPackageName();
+        }
+        try {
+            service.getTopics(
+                    new GetTopicsParam.Builder()
+                            .setAppPackageName(appPackageName)
+                            .setSdkName(sdkName)
+                            .setSdkPackageName(sdkPackageName)
+                            .setShouldRecordObservation(getTopicsRequest.shouldRecordObservation())
+                            .build(),
+                    callerMetadata,
+                    new IGetTopicsCallback.Stub() {
+                        @Override
+                        public void onResult(GetTopicsResult resultParcel) {
+                            executor.execute(
+                                    () -> {
+                                        if (resultParcel.isSuccess()) {
+                                            callback.onResult(buildGetTopicsResponse(resultParcel));
+                                        } else {
+                                            // TODO: Errors should be returned in onFailure method.
+                                            callback.onError(
+                                                    AdServicesStatusUtils.asException(
+                                                            resultParcel));
+                                        }
+                                    });
+                        }
+
+                        @Override
+                        public void onFailure(int resultCode) {
+                            executor.execute(
+                                    () ->
+                                            callback.onError(
+                                                    AdServicesStatusUtils.asException(resultCode)));
+                        }
+                    });
+        } catch (RemoteException e) {
+            sLogger.e(e, "RemoteException");
+            callback.onError(e);
+        }
+    }
+
+    private GetTopicsResponse buildGetTopicsResponse(GetTopicsResult resultParcel) {
+        return new GetTopicsResponse.Builder(
+                        getTopicList(resultParcel), getEncryptedTopicList(resultParcel))
+                .build();
+    }
+
+    private List<Topic> getTopicList(GetTopicsResult resultParcel) {
+        List<Long> taxonomyVersionsList = resultParcel.getTaxonomyVersions();
+        List<Long> modelVersionsList = resultParcel.getModelVersions();
+        List<Integer> topicsCodeList = resultParcel.getTopics();
+        List<Topic> topicList = new ArrayList<>();
+        int size = taxonomyVersionsList.size();
+        for (int i = 0; i < size; i++) {
+            Topic topic =
+                    new Topic(
+                            taxonomyVersionsList.get(i),
+                            modelVersionsList.get(i),
+                            topicsCodeList.get(i));
+            topicList.add(topic);
+        }
+
+        return topicList;
+    }
+
+    private List<EncryptedTopic> getEncryptedTopicList(GetTopicsResult resultParcel) {
+        List<EncryptedTopic> encryptedTopicList = new ArrayList<>();
+        List<byte[]> encryptedTopics = resultParcel.getEncryptedTopics();
+        List<String> encryptionKeys = resultParcel.getEncryptionKeys();
+        List<byte[]> encapsulatedKeys = resultParcel.getEncapsulatedKeys();
+        int size = encryptedTopics.size();
+        for (int i = 0; i < size; i++) {
+            EncryptedTopic encryptedTopic =
+                    new EncryptedTopic(
+                            encryptedTopics.get(i), encryptionKeys.get(i), encapsulatedKeys.get(i));
+            encryptedTopicList.add(encryptedTopic);
+        }
+
+        return encryptedTopicList;
+    }
+
+    /**
+     * If the service is in an APK (as opposed to the system service), unbind it from the service to
+     * allow the APK process to die.
+     *
+     * @hide Not sure if we'll need this functionality in the final API. For now, we need it for
+     *     performance testing to simulate "cold-start" situations.
+     */
+    // TODO: change to @VisibleForTesting
+    @TestApi
+    public void unbindFromService() {
+        mServiceBinder.unbindFromService();
+    }
+}