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/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
+
+}