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