aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-12-04 20:48:28 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-12-04 20:48:28 +0000
commitb8eca3f823ac5e4bb3ddd47be59685b1babe1c7c (patch)
treec31e30fc7f909401abb3ccf92cd0db3b4b3cb1ca
parentbbf7a93091a598e708316b7e8d011fc5b4281561 (diff)
parent15e3ef7b1a426964cf2c2e080a8cbbd02112f6d0 (diff)
downloadOnDevicePersonalization-android14-mainline-adservices-release.tar.gz
Snap for 11174750 from 15e3ef7b1a426964cf2c2e080a8cbbd02112f6d0 to mainline-adservices-releaseaml_ads_341413000android14-mainline-adservices-release
Change-Id: I078c52b0d8be683710981e93e17442a0d0a91df8
-rw-r--r--federatedcompute/apk/AndroidManifest.xml4
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/FederatedComputeManagingServiceImpl.java10
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/common/Constants.java9
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/common/FederatedComputeJobInfo.java (renamed from framework/java/android/adservices/ondevicepersonalization/TrainingExampleInput.aidl)10
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/common/Flags.java35
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/common/PhFlags.java12
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/data/FederatedComputeDbHelper.java9
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/data/FederatedTrainingTaskDao.java60
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/encryption/BackgroundKeyFetchJobService.java174
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/encryption/FederatedComputeEncryptionKeyManager.java311
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/http/CheckinResult.java12
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/http/FederatedComputeHttpRequest.java13
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/http/FederatedComputeHttpResponse.java19
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/http/HttpClient.java37
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/http/HttpClientUtil.java29
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/http/HttpFederatedProtocol.java244
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/http/ProtocolRequestCreator.java28
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/scheduling/FederatedComputeJobManager.java92
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/training/FederatedComputeWorker.java225
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/training/IsolatedTrainingServiceImpl.java11
-rw-r--r--federatedcompute/src/com/android/federatedcompute/services/training/jni/FlRunnerWrapper.java7
-rw-r--r--framework/api/current.txt108
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/DownloadCompletedOutput.aidl19
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/DownloadCompletedOutput.java51
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/DownloadCompletedOutputParcel.java142
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/EventInput.aidl19
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/EventInput.java149
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/EventInputParcel.java220
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/EventLogRecord.java41
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/EventOutput.aidl19
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/EventOutput.java58
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/EventOutputParcel.java141
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/ExecuteInput.aidl19
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/ExecuteInput.java137
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/ExecuteInputParcel.java213
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.aidl19
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.java69
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/ExecuteOutputParcel.java216
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/FederatedComputeInput.java57
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/FederatedComputeScheduler.java26
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/IsolatedService.java87
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/IsolatedWorker.java15
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/LogReader.java16
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java1
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/RenderInput.aidl19
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/RenderInput.java184
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/RenderInputParcel.java274
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/RenderOutput.aidl19
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/RenderOutput.java61
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/RenderOutputParcel.java197
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/RequestLogRecord.java12
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/TrainingExampleOutputParcel.aidl19
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/TrainingExamplesInput.java168
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/TrainingExamplesInputParcel.java (renamed from framework/java/android/adservices/ondevicepersonalization/TrainingExampleInput.java)105
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/TrainingExamplesOutput.java (renamed from framework/java/android/adservices/ondevicepersonalization/TrainingExampleOutput.java)101
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/TrainingExamplesOutputParcel.java (renamed from framework/java/android/adservices/ondevicepersonalization/TrainingExampleOutputParcel.java)78
-rw-r--r--framework/java/android/adservices/ondevicepersonalization/TrainingInterval.java115
-rw-r--r--src/com/android/ondevicepersonalization/services/display/DisplayHelper.java18
-rw-r--r--src/com/android/ondevicepersonalization/services/display/OdpWebViewClient.java22
-rw-r--r--src/com/android/ondevicepersonalization/services/download/OnDevicePersonalizationDataProcessingAsyncCallable.java6
-rw-r--r--src/com/android/ondevicepersonalization/services/federatedcompute/OdpExampleStoreService.java28
-rw-r--r--src/com/android/ondevicepersonalization/services/request/AppRequestFlow.java18
-rw-r--r--src/com/android/ondevicepersonalization/services/request/RenderFlow.java10
-rw-r--r--tests/endtoendtests/OdpClient/AndroidManifest.xml3
-rw-r--r--tests/endtoendtests/OdpClient/src/com/example/odpclient/MainActivity.java83
-rw-r--r--tests/endtoendtests/OdpSampleNetwork/AndroidManifest.xml2
-rw-r--r--tests/endtoendtests/OdpSampleNetwork/res/raw/test_data1.json102
-rw-r--r--tests/endtoendtests/OdpSampleNetwork/src/com/example/odpsamplenetwork/SampleHandler.java138
-rw-r--r--tests/federatedcomputetests/AndroidManifest.xml4
-rw-r--r--tests/federatedcomputetests/src/com/android/federatedcompute/services/FederatedComputeManagingServiceImplTest.java10
-rw-r--r--tests/federatedcomputetests/src/com/android/federatedcompute/services/common/PhFlagsTest.java20
-rw-r--r--tests/federatedcomputetests/src/com/android/federatedcompute/services/data/FederatedTrainingTaskDaoTest.java7
-rw-r--r--tests/federatedcomputetests/src/com/android/federatedcompute/services/encryption/BackgroundKeyFetchJobServiceTest.java243
-rw-r--r--tests/federatedcomputetests/src/com/android/federatedcompute/services/encryption/FederatedComputeKeyFetchManagerTest.java452
-rw-r--r--tests/federatedcomputetests/src/com/android/federatedcompute/services/http/FederatedComputeHttpRequestTest.java73
-rw-r--r--tests/federatedcomputetests/src/com/android/federatedcompute/services/http/FederatedComputeHttpResponseTest.java21
-rw-r--r--tests/federatedcomputetests/src/com/android/federatedcompute/services/http/HttpClientTest.java94
-rw-r--r--tests/federatedcomputetests/src/com/android/federatedcompute/services/http/HttpClientUtilTest.java54
-rw-r--r--tests/federatedcomputetests/src/com/android/federatedcompute/services/http/HttpFederatedProtocolTest.java184
-rw-r--r--tests/federatedcomputetests/src/com/android/federatedcompute/services/http/ProtocolRequestCreatorTest.java13
-rw-r--r--tests/federatedcomputetests/src/com/android/federatedcompute/services/training/FederatedComputeWorkerTest.java162
-rw-r--r--tests/frameworktests/src/android/adservices/ondevicepersonalization/IsolatedServiceTest.java85
-rw-r--r--tests/frameworktests/src/android/adservices/ondevicepersonalization/LogReaderTest.java47
-rw-r--r--tests/frameworktests/src/android/adservices/ondevicepersonalization/OnDevicePersonalizationFrameworkClassesTest.java82
-rw-r--r--tests/perftests/scenarios/src/android/federatedcompute/test/scenario/federatedcompute/ScheduleAndForceTraining.java3
-rw-r--r--tests/perftests/scenarios/src/android/federatedcompute/test/scenario/federatedcompute/TestHelper.java28
-rw-r--r--tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/DownloadHelper.java9
-rw-r--r--tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/DownloadVendorData.java1
-rw-r--r--tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/ReportConversion.java65
-rw-r--r--tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/RequestAd.java1
-rw-r--r--tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/RequestAdAndClickAd.java1
-rw-r--r--tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/RequestAdWithTestAppRotations.java20
-rw-r--r--tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/TestAppHelper.java86
-rw-r--r--tests/perftests/scenarios/tests/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/ReportConversionMicrobenchmark.java36
-rw-r--r--tests/servicetests/src/com/android/ondevicepersonalization/services/display/DisplayHelperTest.java7
-rw-r--r--tests/servicetests/src/com/android/ondevicepersonalization/services/request/RenderFlowTest.java5
-rw-r--r--tests/servicetests/src/com/test/TestPersonalizationHandler.java16
97 files changed, 5017 insertions, 1787 deletions
diff --git a/federatedcompute/apk/AndroidManifest.xml b/federatedcompute/apk/AndroidManifest.xml
index dec46ab3..652dc05a 100644
--- a/federatedcompute/apk/AndroidManifest.xml
+++ b/federatedcompute/apk/AndroidManifest.xml
@@ -48,6 +48,10 @@
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
+ <service android:name="com.android.federatedcompute.services.encryption.BackgroundKeyFetchJobService"
+ android:exported="false"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
<service android:name="com.android.federatedcompute.services.training.IsolatedTrainingService"
android:isolatedProcess="true" android:exported="false" >
</service>
diff --git a/federatedcompute/src/com/android/federatedcompute/services/FederatedComputeManagingServiceImpl.java b/federatedcompute/src/com/android/federatedcompute/services/FederatedComputeManagingServiceImpl.java
index 7d8db4d8..a72819b7 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/FederatedComputeManagingServiceImpl.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/FederatedComputeManagingServiceImpl.java
@@ -20,6 +20,9 @@ import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
+import com.android.federatedcompute.services.common.Flags;
+import com.android.federatedcompute.services.common.FlagsFactory;
+import com.android.federatedcompute.services.encryption.BackgroundKeyFetchJobService;
import com.android.federatedcompute.services.statsd.FederatedComputeStatsdLogger;
import java.util.Objects;
@@ -28,6 +31,12 @@ import java.util.Objects;
public class FederatedComputeManagingServiceImpl extends Service {
private FederatedComputeManagingServiceDelegate mFcpServiceDelegate;
+ private Flags mFlags;
+
+ public FederatedComputeManagingServiceImpl() {
+ mFlags = FlagsFactory.getFlags();
+ }
+
@Override
public void onCreate() {
super.onCreate();
@@ -35,6 +44,7 @@ public class FederatedComputeManagingServiceImpl extends Service {
mFcpServiceDelegate =
new FederatedComputeManagingServiceDelegate(
this, FederatedComputeStatsdLogger.getInstance());
+ BackgroundKeyFetchJobService.scheduleJobIfNeeded(this, mFlags);
}
}
diff --git a/federatedcompute/src/com/android/federatedcompute/services/common/Constants.java b/federatedcompute/src/com/android/federatedcompute/services/common/Constants.java
index 33b64603..6c697525 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/common/Constants.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/common/Constants.java
@@ -36,5 +36,14 @@ public class Constants {
public static final String ISOLATED_TRAINING_SERVICE_NAME =
"com.android.federatedcompute.services.training.IsolatedTrainingService";
+ public static final String TRACE_HTTP_ISSUE_CHECKIN = "Http#issueCheckin";
+ public static final String TRACE_HTTP_REPORT_RESULT = "Http#reportResult";
+ public static final String TRACE_ISOLATED_PROCESS_RUN_FL_TRAINING =
+ "IsolatedProcess#runFlTraining";
+ public static final String TRACE_NATIVE_RUN_FEDERATED_COMPUTATION =
+ "Native#runFederatedComputation";
+ public static final String TRACE_WORKER_RUN_FL_COMPUTATION = "Worker#runFlComputation";
+ public static final String TRACE_WORKER_START_TRAINING_RUN = "Worker#startTrainingRun";
+
private Constants() {}
}
diff --git a/framework/java/android/adservices/ondevicepersonalization/TrainingExampleInput.aidl b/federatedcompute/src/com/android/federatedcompute/services/common/FederatedComputeJobInfo.java
index 7d841a50..7df5d821 100644
--- a/framework/java/android/adservices/ondevicepersonalization/TrainingExampleInput.aidl
+++ b/federatedcompute/src/com/android/federatedcompute/services/common/FederatedComputeJobInfo.java
@@ -14,6 +14,12 @@
* limitations under the License.
*/
-package android.adservices.ondevicepersonalization;
+package com.android.federatedcompute.services.common;
-parcelable TrainingExampleInput;
+public class FederatedComputeJobInfo {
+
+ private FederatedComputeJobInfo() {}
+
+ /** JOB ID to periodically download encryption key. */
+ public static final int ENCRYPTION_KEY_FETCH_JOB_ID = 1000;
+}
diff --git a/federatedcompute/src/com/android/federatedcompute/services/common/Flags.java b/federatedcompute/src/com/android/federatedcompute/services/common/Flags.java
index 5c01501e..6af7c1ca 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/common/Flags.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/common/Flags.java
@@ -16,6 +16,8 @@
package com.android.federatedcompute.services.common;
+import java.util.concurrent.TimeUnit;
+
/** FederatedCompute feature flags interface. This Flags interface hold the default values */
public interface Flags {
/**
@@ -29,7 +31,10 @@ public interface Flags {
return FEDERATED_COMPUTE_GLOBAL_KILL_SWITCH;
}
- /** Flags for {@link FederatedComputeJobManager}. */
+ /**
+ * Flags for {@link
+ * com.android.federatedcompute.services.scheduling.FederatedComputeJobManager}.
+ */
long DEFAULT_SCHEDULING_PERIOD_SECS = 60 * 5; // 5 minutes
default long getDefaultSchedulingPeriodSecs() {
@@ -80,7 +85,7 @@ public interface Flags {
return TRANSIENT_ERROR_RETRY_DELAY_SECS;
}
- /** Flags for {@link FederatedExampleIterator}. */
+ /** Flags for ExampleStoreService. */
long APP_HOSTED_EXAMPLE_STORE_TIMEOUT_SECS = 30;
default long getAppHostedExampleStoreTimeoutSecs() {
@@ -135,4 +140,30 @@ public interface Flags {
default long getTrainingConditionCheckThrottlePeriodMillis() {
return TRAINING_CONDITION_CHECK_THROTTLE_PERIOD_MILLIS;
}
+
+ String ENCRYPTION_KEY_FETCH_URL =
+ "https://fake-coordinator/v1alpha/publicKeys";
+
+ /**
+ * @return Url to fetch encryption key for federated compute.
+ */
+ default String getEncryptionKeyFetchUrl() {
+ return ENCRYPTION_KEY_FETCH_URL;
+ }
+
+ Long FEDERATED_COMPUTE_ENCRYPTION_KEY_MAX_AGE_SECONDS =
+ TimeUnit.DAYS.toSeconds(14/* duration= */);
+
+ /**
+ * @return default max age in seconds for federated compute ecryption keys.
+ */
+ default Long getFederatedComputeEncryptionKeyMaxAgeSeconds() {
+ return FEDERATED_COMPUTE_ENCRYPTION_KEY_MAX_AGE_SECONDS;
+ }
+
+ Long ENCRYPTION_KEY_FETCH_PERIOD_SECONDS = 60 * 60 * 24L; // every 24 h
+
+ default Long getEncryptionKeyFetchPeriodSeconds() {
+ return ENCRYPTION_KEY_FETCH_PERIOD_SECONDS;
+ }
}
diff --git a/federatedcompute/src/com/android/federatedcompute/services/common/PhFlags.java b/federatedcompute/src/com/android/federatedcompute/services/common/PhFlags.java
index 1072965b..ade34909 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/common/PhFlags.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/common/PhFlags.java
@@ -35,6 +35,9 @@ public final class PhFlags implements Flags {
// OnDevicePersonalization Namespace String from DeviceConfig class
static final String NAMESPACE_ON_DEVICE_PERSONALIZATION = "on_device_personalization";
+
+ static final String FEDERATED_COMPUTATION_ENCRYPTION_KEY_DOWNLOAD_URL =
+ "fcp_encryption_key_download_url";
private static final PhFlags sSingleton = new PhFlags();
/** Returns the singleton instance of the PhFlags. */
@@ -60,4 +63,13 @@ public final class PhFlags implements Flags {
static String getSystemPropertyName(String key) {
return SYSTEM_PROPERTY_PREFIX + key;
}
+
+ @Override
+ public String getEncryptionKeyFetchUrl() {
+ return DeviceConfig.getString(
+ /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
+ /* name= */ FEDERATED_COMPUTATION_ENCRYPTION_KEY_DOWNLOAD_URL,
+ /* defaultValue= */ ENCRYPTION_KEY_FETCH_URL
+ );
+ }
}
diff --git a/federatedcompute/src/com/android/federatedcompute/services/data/FederatedComputeDbHelper.java b/federatedcompute/src/com/android/federatedcompute/services/data/FederatedComputeDbHelper.java
index 92ff384c..1ed49974 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/data/FederatedComputeDbHelper.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/data/FederatedComputeDbHelper.java
@@ -54,7 +54,7 @@ public class FederatedComputeDbHelper extends SQLiteOpenHelper {
+ FederatedTrainingTaskColumns.CONTEXT_DATA
+ " BLOB, "
+ FederatedTrainingTaskColumns.CREATION_TIME
- + " INTEGER, "
+ + " INTEGER NOT NULL, "
+ FederatedTrainingTaskColumns.LAST_SCHEDULED_TIME
+ " INTEGER, "
+ FederatedTrainingTaskColumns.LAST_RUN_START_TIME
@@ -62,11 +62,14 @@ public class FederatedComputeDbHelper extends SQLiteOpenHelper {
+ FederatedTrainingTaskColumns.LAST_RUN_END_TIME
+ " INTEGER, "
+ FederatedTrainingTaskColumns.EARLIEST_NEXT_RUN_TIME
- + " INTEGER, "
+ + " INTEGER NOT NULL, "
+ FederatedTrainingTaskColumns.CONSTRAINTS
+ " BLOB, "
+ FederatedTrainingTaskColumns.SCHEDULING_REASON
- + " INTEGER )";
+ + " INTEGER, "
+ + "UNIQUE("
+ + FederatedTrainingTaskColumns.JOB_SCHEDULER_JOB_ID
+ + "))";
private static final String CREATE_ENCRYPTION_KEY_TABLE =
"CREATE TABLE "
diff --git a/federatedcompute/src/com/android/federatedcompute/services/data/FederatedTrainingTaskDao.java b/federatedcompute/src/com/android/federatedcompute/services/data/FederatedTrainingTaskDao.java
index 409505f7..69ca70a0 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/data/FederatedTrainingTaskDao.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/data/FederatedTrainingTaskDao.java
@@ -21,6 +21,7 @@ import static com.android.federatedcompute.services.data.FederatedTraningTaskCon
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
@@ -84,11 +85,20 @@ public class FederatedTrainingTaskDao {
/** Insert a training task or update it if task already exists. */
public boolean updateOrInsertFederatedTrainingTask(FederatedTrainingTask trainingTask) {
- SQLiteDatabase db = getWritableDatabase();
- if (db == null) {
- throw new SQLiteException("Failed to open database.");
+ try {
+ SQLiteDatabase db = getWritableDatabase();
+ if (db == null) {
+ return false;
+ }
+ return trainingTask.addToDatabase(db);
+ } catch (SQLException e) {
+ LogUtil.e(
+ TAG,
+ e,
+ "Failed to persist federated training task %s",
+ trainingTask.populationName());
+ return false;
}
- return trainingTask.addToDatabase(db);
}
/** Get the list of tasks that match select conditions. */
@@ -109,10 +119,15 @@ public class FederatedTrainingTaskDao {
String[] selectionArgs = selectionArgs(jobId);
FederatedTrainingTask task =
Iterables.getOnlyElement(getFederatedTrainingTask(selection, selectionArgs), null);
- if (task != null) {
- deleteFederatedTrainingTask(selection, selectionArgs);
+ try {
+ if (task != null) {
+ deleteFederatedTrainingTask(selection, selectionArgs);
+ }
+ return task;
+ } catch (SQLException e) {
+ LogUtil.e(TAG, e, "Failed to delete federated training task by job id %d", jobId);
+ return null;
}
- return task;
}
/** Delete a task from table based on population name. */
@@ -121,10 +136,19 @@ public class FederatedTrainingTaskDao {
String[] selectionArgs = {populationName};
FederatedTrainingTask task =
Iterables.getOnlyElement(getFederatedTrainingTask(selection, selectionArgs), null);
- if (task != null) {
- deleteFederatedTrainingTask(selection, selectionArgs);
+ try {
+ if (task != null) {
+ deleteFederatedTrainingTask(selection, selectionArgs);
+ }
+ return task;
+ } catch (SQLException e) {
+ LogUtil.e(
+ TAG,
+ e,
+ "Failed to delete federated training task by population name %s",
+ populationName);
+ return null;
}
- return task;
}
/** Delete a task from table based on population name and job scheduler id. */
@@ -138,10 +162,20 @@ public class FederatedTrainingTaskDao {
String[] selectionArgs = {populationName, String.valueOf(jobId)};
FederatedTrainingTask task =
Iterables.getOnlyElement(getFederatedTrainingTask(selection, selectionArgs), null);
- if (task != null) {
- deleteFederatedTrainingTask(selection, selectionArgs);
+ try {
+ if (task != null) {
+ deleteFederatedTrainingTask(selection, selectionArgs);
+ }
+ return task;
+ } catch (SQLException e) {
+ LogUtil.e(
+ TAG,
+ e,
+ "Failed to delete federated training task by population name %s and job id %d",
+ populationName,
+ jobId);
+ return null;
}
- return task;
}
private String[] selectionArgs(Number... args) {
diff --git a/federatedcompute/src/com/android/federatedcompute/services/encryption/BackgroundKeyFetchJobService.java b/federatedcompute/src/com/android/federatedcompute/services/encryption/BackgroundKeyFetchJobService.java
new file mode 100644
index 00000000..6b8287ae
--- /dev/null
+++ b/federatedcompute/src/com/android/federatedcompute/services/encryption/BackgroundKeyFetchJobService.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.federatedcompute.services.encryption;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+
+import com.android.federatedcompute.internal.util.LogUtil;
+import com.android.federatedcompute.services.common.FederatedComputeExecutors;
+import com.android.federatedcompute.services.common.FederatedComputeJobInfo;
+import com.android.federatedcompute.services.common.Flags;
+import com.android.federatedcompute.services.common.FlagsFactory;
+import com.android.federatedcompute.services.data.FederatedComputeEncryptionKey;
+import com.android.internal.annotations.VisibleForTesting;
+
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+public class BackgroundKeyFetchJobService extends JobService {
+ private static final String TAG = BackgroundKeyFetchJobService.class.getSimpleName();
+
+ private static final int ENCRYPTION_KEY_FETCH_JOB_ID =
+ FederatedComputeJobInfo.ENCRYPTION_KEY_FETCH_JOB_ID;
+
+ static class Injector {
+ ListeningExecutorService getExecutor() {
+ return FederatedComputeExecutors.getBackgroundExecutor();
+ }
+
+ ListeningExecutorService getLightWeightExecutor() {
+ return FederatedComputeExecutors.getLightweightExecutor();
+ }
+
+ FederatedComputeEncryptionKeyManager getEncryptionKeyManager(Context context) {
+ return FederatedComputeEncryptionKeyManager.getInstance(context);
+ }
+ }
+
+ private final Injector mInjector;
+
+ @VisibleForTesting
+ BackgroundKeyFetchJobService(Injector injector) {
+ mInjector = injector;
+ }
+
+ /** Runs the background key fetch and persist keys job. The method is for testing only. */
+ @VisibleForTesting
+ public void run(JobParameters params) {
+ var unused = Futures.submit(() -> onStartJob(params), mInjector.getExecutor());
+ }
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ LogUtil.d(TAG, "BackgroundKeyFetchJobService.onStartJob %d", params.getJobId());
+ if (FlagsFactory.getFlags().getGlobalKillSwitch()) {
+ LogUtil.d(TAG, "GlobalKillSwitch enabled, finishing job.");
+ jobFinished(params, false /* wantsReschedule= */);
+ return true;
+ }
+ mInjector
+ .getEncryptionKeyManager(this)
+ .fetchAndPersistActiveKeys(FederatedComputeEncryptionKey.KEY_TYPE_ENCRYPTION,
+ /* isScheduledJob= */ true)
+ .addCallback(
+ new FutureCallback<List<FederatedComputeEncryptionKey>>() {
+ @Override
+ public void onSuccess(
+ List<FederatedComputeEncryptionKey>
+ federatedComputeEncryptionKeys) {
+ LogUtil.d(
+ TAG,
+ "BackgroundKeyFetchJobService %d is done, fetched %d keys",
+ params.getJobId(),
+ federatedComputeEncryptionKeys.size());
+ jobFinished(params, false/* wantsReschedule= */);
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ LogUtil.e(
+ TAG,
+ "Failed to run job %d to fetch key and delete expired keys",
+ params.getJobId());
+ if (throwable instanceof ExecutionException) {
+ LogUtil.e(
+ TAG,
+ "Background key fetch failed due to internal error");
+ } else if (throwable instanceof TimeoutException) {
+ LogUtil.e(
+ TAG,
+ "Background key fetch failed due to timeout error");
+ } else if (throwable instanceof InterruptedException) {
+ LogUtil.e(
+ TAG,
+ "Background key fetch failed due to interruption "
+ + "error");
+ } else {
+ LogUtil.e(
+ TAG,
+ "Background key fetch failed due to unexpected error");
+ }
+ jobFinished(params, false /* wantsReschedule= */);
+ }
+ },
+ mInjector.getLightWeightExecutor());
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ LogUtil.d(TAG, "BackgroundKeyFetchJobService.onStopJob %d", params.getJobId());
+ return false;
+ }
+
+ /** Schedule the periodic background key fetch and delete job if it is not scheduled. */
+ public static boolean scheduleJobIfNeeded(Context context, Flags flags) {
+ final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+ if (jobScheduler == null) {
+ LogUtil.e(TAG, "Failed to get job scheduler from system service.");
+ return false;
+ }
+
+ final JobInfo scheduledJob = jobScheduler.getPendingJob(ENCRYPTION_KEY_FETCH_JOB_ID);
+ final JobInfo jobInfo =
+ new JobInfo.Builder(
+ ENCRYPTION_KEY_FETCH_JOB_ID,
+ new ComponentName(context, BackgroundKeyFetchJobService.class))
+ .setPeriodic(
+ flags.getEncryptionKeyFetchPeriodSeconds()
+ * 1000) // convert to milliseconds
+ .setRequiresBatteryNotLow(true)
+ .setRequiresDeviceIdle(true)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
+ .build();
+
+ if (!jobInfo.equals(scheduledJob)) {
+ jobScheduler.schedule(jobInfo);
+ LogUtil.d(
+ TAG,
+ "Scheduled job BackgroundKeyFetchJobService id %d",
+ ENCRYPTION_KEY_FETCH_JOB_ID);
+ return true;
+ } else {
+ LogUtil.d(
+ TAG,
+ "Already scheduled job BackgroundKeyFetchJobService id %d",
+ ENCRYPTION_KEY_FETCH_JOB_ID);
+ return false;
+ }
+ }
+}
diff --git a/federatedcompute/src/com/android/federatedcompute/services/encryption/FederatedComputeEncryptionKeyManager.java b/federatedcompute/src/com/android/federatedcompute/services/encryption/FederatedComputeEncryptionKeyManager.java
new file mode 100644
index 00000000..09df11c7
--- /dev/null
+++ b/federatedcompute/src/com/android/federatedcompute/services/encryption/FederatedComputeEncryptionKeyManager.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.federatedcompute.services.encryption;
+
+import android.content.Context;
+
+import com.android.federatedcompute.internal.util.LogUtil;
+import com.android.federatedcompute.services.common.Clock;
+import com.android.federatedcompute.services.common.FederatedComputeExecutors;
+import com.android.federatedcompute.services.common.Flags;
+import com.android.federatedcompute.services.common.FlagsFactory;
+import com.android.federatedcompute.services.common.MonotonicClock;
+import com.android.federatedcompute.services.data.FederatedComputeEncryptionKey;
+import com.android.federatedcompute.services.data.FederatedComputeEncryptionKeyDao;
+import com.android.federatedcompute.services.http.FederatedComputeHttpRequest;
+import com.android.federatedcompute.services.http.FederatedComputeHttpResponse;
+import com.android.federatedcompute.services.http.HttpClient;
+import com.android.federatedcompute.services.http.HttpClientUtil;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.FluentFuture;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/** Class to manage key fetch. */
+public class FederatedComputeEncryptionKeyManager {
+ private static final String TAG = "FederatedComputeEncryptionKeyManager";
+
+ private interface EncryptionKeyResponseContract {
+ String RESPONSE_HEADER_CACHE_CONTROL_LABEL = "cache-control";
+ String RESPONSE_HEADER_AGE_LABEL = "age";
+
+ String RESPONSE_HEADER_CACHE_CONTROL_MAX_AGE_LABEL = "max-age=";
+
+ String RESPONSE_KEYS_LABEL = "keys";
+
+ String RESPONSE_KEY_ID_LABEL = "id";
+
+ String RESPONSE_PUBLIC_KEY = "key";
+ }
+
+ @VisibleForTesting private final FederatedComputeEncryptionKeyDao mEncryptionKeyDao;
+
+ private static volatile FederatedComputeEncryptionKeyManager sBackgroundKeyManager;
+
+ private final Clock mClock;
+
+ private final Flags mFlags;
+
+ private final HttpClient mHttpClient;
+
+ private final ExecutorService mBackgroundExecutor;
+
+ public FederatedComputeEncryptionKeyManager(
+ Clock clock,
+ FederatedComputeEncryptionKeyDao encryptionKeyDao,
+ Flags flags,
+ HttpClient httpClient,
+ ExecutorService backgroundExecutor) {
+ mClock = clock;
+ mEncryptionKeyDao = encryptionKeyDao;
+ mFlags = flags;
+ mHttpClient = httpClient;
+ mBackgroundExecutor = backgroundExecutor;
+ }
+
+ /**
+ * @return a singleton instance for key manager
+ */
+ public static FederatedComputeEncryptionKeyManager getInstance(Context context) {
+ if (sBackgroundKeyManager == null) {
+ synchronized (FederatedComputeEncryptionKeyManager.class) {
+ if (sBackgroundKeyManager == null) {
+ FederatedComputeEncryptionKeyDao encryptionKeyDao =
+ FederatedComputeEncryptionKeyDao.getInstance(context);
+ HttpClient client = new HttpClient();
+ Clock clock = MonotonicClock.getInstance();
+ Flags flags = FlagsFactory.getFlags();
+ sBackgroundKeyManager =
+ new FederatedComputeEncryptionKeyManager(
+ clock,
+ encryptionKeyDao,
+ flags,
+ client,
+ FederatedComputeExecutors.getBackgroundExecutor());
+ }
+ }
+ }
+ return sBackgroundKeyManager;
+ }
+
+ /** For testing only, returns an instance of key manager for test. */
+ @VisibleForTesting
+ public static FederatedComputeEncryptionKeyManager getInstanceForTest(
+ Clock clock,
+ FederatedComputeEncryptionKeyDao encryptionKeyDao,
+ Flags flags,
+ HttpClient client,
+ ExecutorService executor) {
+ if (sBackgroundKeyManager == null) {
+ synchronized (FederatedComputeEncryptionKeyManager.class) {
+ if (sBackgroundKeyManager == null) {
+ sBackgroundKeyManager =
+ new FederatedComputeEncryptionKeyManager(
+ clock, encryptionKeyDao, flags, client, executor);
+ }
+ }
+ }
+ return sBackgroundKeyManager;
+ }
+
+ /**
+ * Fetch the active key from the server, persists the fetched key to encryption_key table, and
+ * deletes expired keys
+ */
+ public FluentFuture<List<FederatedComputeEncryptionKey>> fetchAndPersistActiveKeys(
+ @FederatedComputeEncryptionKey.KeyType int keyType, boolean isScheduledJob) {
+ String fetchUri = mFlags.getEncryptionKeyFetchUrl();
+ if (fetchUri == null) {
+ throw new IllegalArgumentException("Url to fetch active encryption keys is null");
+ }
+
+ FederatedComputeHttpRequest request =
+ FederatedComputeHttpRequest.create(
+ fetchUri,
+ HttpClientUtil.HttpMethod.GET,
+ new HashMap<String, String>(),
+ HttpClientUtil.EMPTY_BODY);
+
+ return FluentFuture.from(mHttpClient.performRequestAsyncWithRetry(request))
+ .transform(
+ response ->
+ parseFetchEncryptionKeyPayload(
+ response, keyType, mClock.currentTimeMillis()),
+ mBackgroundExecutor)
+ .transform(
+ result -> {
+ result.forEach(mEncryptionKeyDao::insertEncryptionKey);
+ if (isScheduledJob) {
+ // When the job is a background scheduled job, delete the expired
+ // keys, otherwise, only fetch from the key server.
+ mEncryptionKeyDao.deleteExpiredKeys();
+ }
+ return result;
+ },
+ mBackgroundExecutor); // TODO: Add timeout controlled by Ph flags
+ }
+
+ private ImmutableList<FederatedComputeEncryptionKey> parseFetchEncryptionKeyPayload(
+ FederatedComputeHttpResponse keyFetchResponse,
+ @FederatedComputeEncryptionKey.KeyType int keyType,
+ Long fetchTime) {
+ String payload = new String(Objects.requireNonNull(keyFetchResponse.getPayload()));
+ Map<String, List<String>> headers = keyFetchResponse.getHeaders();
+ long ttlInSeconds = getTTL(headers);
+ if (ttlInSeconds <= 0) {
+ ttlInSeconds = mFlags.getFederatedComputeEncryptionKeyMaxAgeSeconds();
+ }
+
+ try {
+ JSONObject responseObj = new JSONObject(payload);
+ JSONArray keysArr =
+ responseObj.getJSONArray(EncryptionKeyResponseContract.RESPONSE_KEYS_LABEL);
+ ImmutableList.Builder<FederatedComputeEncryptionKey> encryptionKeys =
+ ImmutableList.builder();
+
+ for (int i = 0; i < keysArr.length(); i++) {
+ JSONObject keyObj = keysArr.getJSONObject(i);
+ FederatedComputeEncryptionKey key =
+ new FederatedComputeEncryptionKey.Builder()
+ .setKeyIdentifier(
+ keyObj.getString(
+ EncryptionKeyResponseContract
+ .RESPONSE_KEY_ID_LABEL))
+ .setPublicKey(
+ keyObj.getString(
+ EncryptionKeyResponseContract.RESPONSE_PUBLIC_KEY))
+ .setKeyType(keyType)
+ .setCreationTime(fetchTime)
+ .setExpiryTime(
+ fetchTime + ttlInSeconds * 1000) // convert to milliseconds
+ .build();
+ encryptionKeys.add(key);
+ }
+ return encryptionKeys.build();
+ } catch (JSONException e) {
+ LogUtil.e(TAG, "Invalid Json response: " + e.getMessage());
+ return ImmutableList.of();
+ }
+ }
+
+ /**
+ * Parse the "age" and "cache-control" of response headers. Calculate the ttl of the current key
+ * maxage (in cache-control) - age.
+ *
+ * @return the ttl in seconds of the keys.
+ */
+ @VisibleForTesting
+ static long getTTL(Map<String, List<String>> headers) {
+ String cacheControl = null;
+ int cachedAge = 0;
+ int remainingHeaders = 2;
+ for (String key : headers.keySet()) {
+ if (key != null) {
+ if (key.equalsIgnoreCase(
+ EncryptionKeyResponseContract.RESPONSE_HEADER_CACHE_CONTROL_LABEL)) {
+ List<String> field = headers.get(key);
+ if (field != null && field.size() > 0) {
+ cacheControl = field.get(0).toLowerCase(Locale.ENGLISH);
+ remainingHeaders -= 1;
+ }
+
+ } else if (key.equalsIgnoreCase(
+ EncryptionKeyResponseContract.RESPONSE_HEADER_AGE_LABEL)) {
+ List<String> field = headers.get(key);
+ if (field != null && field.size() > 0) {
+ try {
+ cachedAge = Integer.parseInt(field.get(0));
+ } catch (NumberFormatException e) {
+ LogUtil.e(TAG, "Error parsing age header");
+ }
+ remainingHeaders -= 1;
+ }
+ }
+ }
+ if (remainingHeaders == 0) {
+ break;
+ }
+ }
+ if (cacheControl == null) {
+ LogUtil.d(TAG, "Cache-Control header or value is missing");
+ return 0;
+ }
+
+ String[] tokens = cacheControl.split(",", /* limit= */ 0);
+ long maxAge = 0;
+ for (String s : tokens) {
+ String token = s.trim();
+ if (token.startsWith(
+ EncryptionKeyResponseContract.RESPONSE_HEADER_CACHE_CONTROL_MAX_AGE_LABEL)) {
+ try {
+ maxAge =
+ Long.parseLong(
+ token.substring(
+ /* beginIndex= */ EncryptionKeyResponseContract
+ .RESPONSE_HEADER_CACHE_CONTROL_MAX_AGE_LABEL
+ .length())); // in the format of
+ // "max-age=<number>"
+ } catch (NumberFormatException e) {
+ LogUtil.d(TAG, "Failed to parse max-age value");
+ return 0;
+ }
+ }
+ }
+ if (maxAge == 0) {
+ LogUtil.d(TAG, "max-age directive is missing");
+ return 0;
+ }
+ return maxAge - cachedAge;
+ }
+
+ /** Get active keys, if there is no active key, then force a fetch from the key service.
+ * In the case of key fetching from the key service, the http call
+ * is executed on a BlockingExecutor.
+ * @return The list of active keys.
+ */
+ public List<FederatedComputeEncryptionKey> getOrFetchActiveKeys(int keyType, int keyCount) {
+ List<FederatedComputeEncryptionKey> activeKeys = mEncryptionKeyDao
+ .getLatestExpiryNKeys(keyCount);
+ if (activeKeys.size() > 0) {
+ return activeKeys;
+ }
+ try {
+ var fetchedKeysUnused = fetchAndPersistActiveKeys(keyType,
+ /* isScheduledJob= */ false).get(1, TimeUnit.SECONDS);
+ activeKeys = mEncryptionKeyDao.getLatestExpiryNKeys(keyCount);
+ if (activeKeys.size() > 0) {
+ return activeKeys;
+ }
+ } catch (Exception e) {
+ LogUtil.e(TAG, "Exception encountered when forcing encryption key fetch: "
+ + e.getMessage());
+ }
+ return activeKeys;
+ }
+}
diff --git a/federatedcompute/src/com/android/federatedcompute/services/http/CheckinResult.java b/federatedcompute/src/com/android/federatedcompute/services/http/CheckinResult.java
index e82bfe61..00542f4e 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/http/CheckinResult.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/http/CheckinResult.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import com.android.internal.util.Preconditions;
import com.google.internal.federated.plan.ClientOnlyPlan;
+import com.google.internal.federatedcompute.v1.RejectionInfo;
import com.google.ondevicepersonalization.federatedcompute.proto.TaskAssignment;
/**
@@ -30,7 +31,7 @@ public class CheckinResult {
private String mInputCheckpoint = null;
private ClientOnlyPlan mPlanData = null;
private TaskAssignment mTaskAssignment = null;
-
+ private RejectionInfo mRejectionInfo = null;
public CheckinResult(
String inputCheckpoint, ClientOnlyPlan planData, TaskAssignment taskAssignment) {
this.mInputCheckpoint = inputCheckpoint;
@@ -38,6 +39,10 @@ public class CheckinResult {
this.mTaskAssignment = taskAssignment;
}
+ public CheckinResult(RejectionInfo mRejectionInfo) {
+ this.mRejectionInfo = mRejectionInfo;
+ }
+
@Nullable
public String getInputCheckpointFile() {
Preconditions.checkArgument(
@@ -55,4 +60,9 @@ public class CheckinResult {
public TaskAssignment getTaskAssignment() {
return mTaskAssignment;
}
+
+ @Nullable
+ public RejectionInfo getRejectionInfo() {
+ return mRejectionInfo;
+ }
}
diff --git a/federatedcompute/src/com/android/federatedcompute/services/http/FederatedComputeHttpRequest.java b/federatedcompute/src/com/android/federatedcompute/services/http/FederatedComputeHttpRequest.java
index 527811df..80ef1391 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/http/FederatedComputeHttpRequest.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/http/FederatedComputeHttpRequest.java
@@ -16,9 +16,7 @@
package com.android.federatedcompute.services.http;
-import static com.android.federatedcompute.services.http.HttpClientUtil.CONTENT_ENCODING_HDR;
import static com.android.federatedcompute.services.http.HttpClientUtil.CONTENT_LENGTH_HDR;
-import static com.android.federatedcompute.services.http.HttpClientUtil.GZIP_ENCODING_HDR;
import com.android.federatedcompute.services.http.HttpClientUtil.HttpMethod;
@@ -45,18 +43,10 @@ public final class FederatedComputeHttpRequest {
/** Creates a {@link FederatedComputeHttpRequest} based on given inputs. */
public static FederatedComputeHttpRequest create(
- String uri,
- HttpMethod httpMethod,
- HashMap<String, String> extraHeaders,
- byte[] body,
- boolean useCompression) {
+ String uri, HttpMethod httpMethod, HashMap<String, String> extraHeaders, byte[] body) {
if (!uri.startsWith(HTTPS_SCHEMA) && !uri.startsWith(LOCAL_HOST_URI)) {
throw new IllegalArgumentException("Non-HTTPS URIs are not supported: " + uri);
}
- if (useCompression) {
- body = HttpClientUtil.compressWithGzip(body);
- extraHeaders.put(CONTENT_ENCODING_HDR, GZIP_ENCODING_HDR);
- }
if (extraHeaders.containsKey(CONTENT_LENGTH_HDR)) {
throw new IllegalArgumentException("Content-Length header should not be provided!");
}
@@ -67,7 +57,6 @@ public final class FederatedComputeHttpRequest {
}
extraHeaders.put(CONTENT_LENGTH_HDR, String.valueOf(body.length));
}
-
return new FederatedComputeHttpRequest(uri, httpMethod, extraHeaders, body);
}
diff --git a/federatedcompute/src/com/android/federatedcompute/services/http/FederatedComputeHttpResponse.java b/federatedcompute/src/com/android/federatedcompute/services/http/FederatedComputeHttpResponse.java
index 5eb1e920..48338f29 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/http/FederatedComputeHttpResponse.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/http/FederatedComputeHttpResponse.java
@@ -16,16 +16,20 @@
package com.android.federatedcompute.services.http;
+import static com.android.federatedcompute.services.http.HttpClientUtil.CONTENT_ENCODING_HDR;
+import static com.android.federatedcompute.services.http.HttpClientUtil.GZIP_ENCODING_HDR;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** Class to hold FederatedCompute http response. */
public class FederatedComputeHttpResponse {
private Integer mStatusCode;
- private Map<String, List<String>> mHeaders;
+ private Map<String, List<String>> mHeaders = new HashMap<>();
private byte[] mPayload;
private FederatedComputeHttpResponse() {}
@@ -45,6 +49,18 @@ public class FederatedComputeHttpResponse {
return mPayload;
}
+ /** Returns whether http response body is compressed with gzip. */
+ public boolean isResponseCompressed() {
+ if (mHeaders.containsKey(CONTENT_ENCODING_HDR)) {
+ for (String format : mHeaders.get(CONTENT_ENCODING_HDR)) {
+ if (format.contains(GZIP_ENCODING_HDR)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/** Builder for FederatedComputeHttpResponse. */
public static final class Builder {
private final FederatedComputeHttpResponse mHttpResponse;
@@ -71,6 +87,7 @@ public class FederatedComputeHttpResponse {
mHttpResponse.mPayload = payload;
return this;
}
+
/** Build {@link FederatedComputeHttpResponse}. */
public FederatedComputeHttpResponse build() {
if (mHttpResponse.mStatusCode == null) {
diff --git a/federatedcompute/src/com/android/federatedcompute/services/http/HttpClient.java b/federatedcompute/src/com/android/federatedcompute/services/http/HttpClient.java
index 2caeccea..acbd8c0e 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/http/HttpClient.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/http/HttpClient.java
@@ -45,6 +45,7 @@ import java.util.concurrent.TimeUnit;
*/
public class HttpClient {
private static final String TAG = HttpClient.class.getSimpleName();
+ private static final int RETRY_LIMIT = 3;
private static final int NETWORK_CONNECT_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(5);
private static final int NETWORK_READ_TIMEOUT_MS = (int) TimeUnit.SECONDS.toMillis(30);
@@ -60,17 +61,45 @@ public class HttpClient {
return urlConnection;
}
- /** Perform HTTP requests based on given information asynchronously. */
+ /**
+ * Perform HTTP requests based on given information asynchronously with retries in case http
+ * will return not OK response code.
+ */
@NonNull
- public ListenableFuture<FederatedComputeHttpResponse> performRequestAsync(
+ public ListenableFuture<FederatedComputeHttpResponse> performRequestAsyncWithRetry(
FederatedComputeHttpRequest request) {
try {
- return getBlockingExecutor().submit(() -> performRequest(request));
+ return getBlockingExecutor().submit(() -> performRequestWithRetry(request));
} catch (Exception e) {
return Futures.immediateFailedFuture(e);
}
}
+ /** Perform HTTP requests based on given information with retries. */
+ @NonNull
+ public FederatedComputeHttpResponse performRequestWithRetry(FederatedComputeHttpRequest request)
+ throws IOException {
+ int count = 0;
+ FederatedComputeHttpResponse response = null;
+ while (count < RETRY_LIMIT) {
+ try {
+ response = performRequest(request);
+ if (HTTP_OK_STATUS.contains(response.getStatusCode())) {
+ return response;
+ }
+ // we want to continue retry in case it is IO exception.
+ } catch (IOException e) {
+ // propagate IO exception after RETRY_LIMIT times attempt.
+ if (count >= RETRY_LIMIT - 1) {
+ throw e;
+ }
+ } finally {
+ count++;
+ }
+ }
+ return response;
+ }
+
/** Perform HTTP requests based on given information. */
@NonNull
public FederatedComputeHttpResponse performRequest(FederatedComputeHttpRequest request)
@@ -93,7 +122,7 @@ public class HttpClient {
urlConnection = (HttpURLConnection) setup(url);
} catch (IOException e) {
LogUtil.e(TAG, e, "Failed to open target URL");
- throw new IllegalArgumentException("Failed to open target URL", e);
+ throw new IOException("Failed to open target URL", e);
}
try {
diff --git a/federatedcompute/src/com/android/federatedcompute/services/http/HttpClientUtil.java b/federatedcompute/src/com/android/federatedcompute/services/http/HttpClientUtil.java
index 1d6bf784..61d719e9 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/http/HttpClientUtil.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/http/HttpClientUtil.java
@@ -21,23 +21,25 @@ import com.android.federatedcompute.internal.util.LogUtil;
import com.google.common.collect.ImmutableSet;
import com.google.protobuf.ByteString;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
/** Utility class containing http related variable e.g. headers, method. */
public final class HttpClientUtil {
private static final String TAG = HttpClientUtil.class.getSimpleName();
- public static final String IDENTITY_ENCODING_HDR = "identity";
public static final String CONTENT_ENCODING_HDR = "Content-Encoding";
+
+ public static final String ACCEPT_ENCODING_HDR = "Accept-Encoding";
public static final String CONTENT_LENGTH_HDR = "Content-Length";
public static final String GZIP_ENCODING_HDR = "gzip";
- public static final String API_KEY_HDR = "x-goog-api-key";
public static final String CONTENT_TYPE_HDR = "Content-Type";
public static final String PROTOBUF_CONTENT_TYPE = "application/x-protobuf";
public static final String OCTET_STREAM = "application/octet-stream";
- public static final String CLIENT_DECODE_GZIP_SUFFIX = "+gzip";
public static final ImmutableSet<Integer> HTTP_OK_STATUS = ImmutableSet.of(200, 201);
- public static final String FAKE_API_KEY = "FAKE_API_KEY";
+ public static final String ODP_IDEMPOTENCY_KEY = "odp-idempotency-key";
public static final int DEFAULT_BUFFER_SIZE = 1024;
public static final byte[] EMPTY_BODY = new byte[0];
@@ -57,7 +59,24 @@ public final class HttpClientUtil {
return outputStream.toByteString().toByteArray();
} catch (IOException e) {
LogUtil.e(TAG, "Failed to compress using Gzip");
- throw new IllegalArgumentException("Failed to compress using Gzip", e);
+ throw new IllegalStateException("Failed to compress using Gzip", e);
+ }
+ }
+
+ /** Uncompresses the input data using Gzip. */
+ public static byte[] uncompressWithGzip(byte[] data) {
+ try (ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+ GZIPInputStream gzip = new GZIPInputStream(inputStream);
+ ByteArrayOutputStream result = new ByteArrayOutputStream()) {
+ int length;
+ byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+ while ((length = gzip.read(buffer, 0, DEFAULT_BUFFER_SIZE)) > 0) {
+ result.write(buffer, 0, length);
+ }
+ return result.toByteArray();
+ } catch (Exception e) {
+ LogUtil.e(TAG, "Failed to decompress the data.", e);
+ throw new IllegalStateException("Failed to unscompress using Gzip", e);
}
}
diff --git a/federatedcompute/src/com/android/federatedcompute/services/http/HttpFederatedProtocol.java b/federatedcompute/src/com/android/federatedcompute/services/http/HttpFederatedProtocol.java
index a55fcf49..1bae7ebd 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/http/HttpFederatedProtocol.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/http/HttpFederatedProtocol.java
@@ -16,12 +16,21 @@
package com.android.federatedcompute.services.http;
+import static com.android.federatedcompute.services.common.Constants.TRACE_HTTP_ISSUE_CHECKIN;
+import static com.android.federatedcompute.services.common.Constants.TRACE_HTTP_REPORT_RESULT;
import static com.android.federatedcompute.services.common.FederatedComputeExecutors.getBackgroundExecutor;
import static com.android.federatedcompute.services.common.FederatedComputeExecutors.getLightweightExecutor;
import static com.android.federatedcompute.services.common.FileUtils.createTempFile;
import static com.android.federatedcompute.services.common.FileUtils.readFileAsByteArray;
import static com.android.federatedcompute.services.common.FileUtils.writeToFile;
+import static com.android.federatedcompute.services.http.HttpClientUtil.ACCEPT_ENCODING_HDR;
+import static com.android.federatedcompute.services.http.HttpClientUtil.GZIP_ENCODING_HDR;
import static com.android.federatedcompute.services.http.HttpClientUtil.HTTP_OK_STATUS;
+import static com.android.federatedcompute.services.http.HttpClientUtil.ODP_IDEMPOTENCY_KEY;
+import static com.android.federatedcompute.services.http.HttpClientUtil.compressWithGzip;
+import static com.android.federatedcompute.services.http.HttpClientUtil.uncompressWithGzip;
+
+import android.os.Trace;
import com.android.federatedcompute.internal.util.LogUtil;
import com.android.federatedcompute.services.http.HttpClientUtil.HttpMethod;
@@ -29,13 +38,15 @@ import com.android.federatedcompute.services.training.util.ComputationResult;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.AsyncCallable;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.internal.federated.plan.ClientOnlyPlan;
import com.google.internal.federatedcompute.v1.ClientVersion;
+import com.google.internal.federatedcompute.v1.RejectionInfo;
import com.google.internal.federatedcompute.v1.Resource;
+import com.google.internal.federatedcompute.v1.ResourceCapabilities;
+import com.google.internal.federatedcompute.v1.ResourceCompressionFormat;
import com.google.ondevicepersonalization.federatedcompute.proto.CreateTaskAssignmentRequest;
import com.google.ondevicepersonalization.federatedcompute.proto.CreateTaskAssignmentResponse;
import com.google.ondevicepersonalization.federatedcompute.proto.ReportResultRequest;
@@ -46,10 +57,12 @@ import com.google.ondevicepersonalization.federatedcompute.proto.UploadInstructi
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.HashMap;
+import java.util.UUID;
+import java.util.concurrent.Callable;
/** Implements a single session of HTTP-based federated compute protocol. */
public final class HttpFederatedProtocol {
- public static final String TAG = "HttpFederatedProtocol";
+ public static final String TAG = HttpFederatedProtocol.class.getSimpleName();
private final String mClientVersion;
private final String mPopulationName;
private final HttpClient mHttpClient;
@@ -64,8 +77,7 @@ public final class HttpFederatedProtocol {
this.mClientVersion = clientVersion;
this.mPopulationName = populationName;
this.mHttpClient = httpClient;
- this.mTaskAssignmentRequestCreator =
- new ProtocolRequestCreator(entryUri, new HashMap<>(), false);
+ this.mTaskAssignmentRequestCreator = new ProtocolRequestCreator(entryUri, new HashMap<>());
}
/** Creates a HttpFederatedProtocol object. */
@@ -76,53 +88,77 @@ public final class HttpFederatedProtocol {
/** Helper function to perform check in and download federated task from remote servers. */
public ListenableFuture<CheckinResult> issueCheckin() {
- ListenableFuture<TaskAssignment> taskAssignmentFuture =
- FluentFuture.from(createTaskAssignment())
- .transform(
- getTaskAssignmentHttpResponse ->
- getTaskAssignment(getTaskAssignmentHttpResponse),
- getLightweightExecutor());
- ListenableFuture<FederatedComputeHttpResponse> planDataResponseFuture =
- FluentFuture.from(taskAssignmentFuture)
- .transformAsync(
- taskAssignment -> fetchTaskResource(taskAssignment.getPlan()),
- getBackgroundExecutor());
- ListenableFuture<FederatedComputeHttpResponse> checkpointDataResponseFuture =
- FluentFuture.from(taskAssignmentFuture)
- .transformAsync(
- taskAssignment ->
- fetchTaskResource(taskAssignment.getInitCheckpoint()),
- getBackgroundExecutor());
- return Futures.whenAllSucceed(
- taskAssignmentFuture, planDataResponseFuture, checkpointDataResponseFuture)
- .callAsync(
- new AsyncCallable<CheckinResult>() {
- @Override
- public ListenableFuture<CheckinResult> call() {
- return getCheckinResult(
- planDataResponseFuture,
- checkpointDataResponseFuture,
- taskAssignmentFuture);
+ Trace.beginAsyncSection(TRACE_HTTP_ISSUE_CHECKIN, 0);
+ return FluentFuture.from(createTaskAssignment())
+ .transformAsync(
+ federatedComputeHttpResponse -> {
+ validateHttpResponseStatus(
+ "Start task assignment", federatedComputeHttpResponse);
+ CreateTaskAssignmentResponse taskAssignmentResponse;
+ try {
+ taskAssignmentResponse =
+ CreateTaskAssignmentResponse.parseFrom(
+ federatedComputeHttpResponse.getPayload());
+ } catch (InvalidProtocolBufferException e) {
+ throw new IllegalStateException(
+ "Could not parse StartTaskAssignmentResponse proto", e);
+ }
+ if (taskAssignmentResponse.hasRejectionInfo()) {
+ return Futures.immediateFuture(
+ new CheckinResult(
+ taskAssignmentResponse.getRejectionInfo()));
}
+ TaskAssignment taskAssignment =
+ getTaskAssignment(taskAssignmentResponse);
+ ListenableFuture<FederatedComputeHttpResponse> planDataResponseFuture =
+ fetchTaskResource(taskAssignment.getPlan());
+ ListenableFuture<FederatedComputeHttpResponse>
+ checkpointDataResponseFuture =
+ fetchTaskResource(taskAssignment.getInitCheckpoint());
+ return Futures.whenAllSucceed(
+ planDataResponseFuture, checkpointDataResponseFuture)
+ .call(
+ new Callable<CheckinResult>() {
+ @Override
+ public CheckinResult call() throws Exception {
+ return getCheckinResult(
+ planDataResponseFuture,
+ checkpointDataResponseFuture,
+ taskAssignment);
+ }
+ },
+ getBackgroundExecutor());
},
getBackgroundExecutor());
}
/** Helper functions to reporting result and upload result. */
- public FluentFuture<Void> reportResult(ComputationResult computationResult) {
+ public FluentFuture<RejectionInfo> reportResult(ComputationResult computationResult) {
+ Trace.beginAsyncSection(TRACE_HTTP_REPORT_RESULT, 0);
if (computationResult != null && computationResult.isResultSuccess()) {
return FluentFuture.from(performReportResult(computationResult))
.transformAsync(
- reportResp ->
- processReportResultResponseAndUploadResult(
- reportResp, computationResult),
- getBackgroundExecutor())
- .transform(
- resp -> {
- validateHttpResponseStatus("Upload result", resp);
- return null;
+ reportResp -> {
+ ReportResultResponse reportResultResponse =
+ getReportResultResponse(reportResp);
+ if (reportResultResponse.hasRejectionInfo()) {
+ return Futures.immediateFuture(
+ reportResultResponse.getRejectionInfo());
+ }
+ return FluentFuture.from(
+ processReportResultResponseAndUploadResult(
+ reportResultResponse, computationResult))
+ .transform(
+ resp -> {
+ validateHttpResponseStatus(
+ "Upload result", resp);
+ Trace.endAsyncSection(
+ TRACE_HTTP_REPORT_RESULT, 0);
+ return null;
+ },
+ getLightweightExecutor());
},
- getLightweightExecutor());
+ getBackgroundExecutor());
} else {
return FluentFuture.from(performReportResult(computationResult))
.transform(
@@ -138,7 +174,13 @@ public final class HttpFederatedProtocol {
CreateTaskAssignmentRequest request =
CreateTaskAssignmentRequest.newBuilder()
.setClientVersion(ClientVersion.newBuilder().setVersionCode(mClientVersion))
+ .setResourceCapabilities(
+ ResourceCapabilities.newBuilder()
+ .addSupportedCompressionFormats(
+ ResourceCompressionFormat
+ .RESOURCE_COMPRESSION_FORMAT_GZIP))
.build();
+
String taskAssignmentUriSuffix =
String.format(
"/taskassignment/v1/population/%1$s:create-task-assignment",
@@ -147,20 +189,16 @@ public final class HttpFederatedProtocol {
mTaskAssignmentRequestCreator.createProtoRequest(
taskAssignmentUriSuffix,
HttpMethod.POST,
+ new HashMap<>(),
request.toByteArray(),
/* isProtobufEncoded= */ true);
- return mHttpClient.performRequestAsync(httpRequest);
+ httpRequest
+ .getExtraHeaders()
+ .put(ODP_IDEMPOTENCY_KEY, System.currentTimeMillis() + " - " + UUID.randomUUID());
+ return mHttpClient.performRequestAsyncWithRetry(httpRequest);
}
- private TaskAssignment getTaskAssignment(FederatedComputeHttpResponse httpResponse) {
- validateHttpResponseStatus("Start task assignment", httpResponse);
- CreateTaskAssignmentResponse taskAssignmentResponse;
- try {
- taskAssignmentResponse =
- CreateTaskAssignmentResponse.parseFrom(httpResponse.getPayload());
- } catch (InvalidProtocolBufferException e) {
- throw new IllegalStateException("Could not parse StartTaskAssignmentResponse proto", e);
- }
+ private TaskAssignment getTaskAssignment(CreateTaskAssignmentResponse taskAssignmentResponse) {
if (taskAssignmentResponse.hasRejectionInfo()) {
throw new IllegalStateException("Device rejected by server.");
}
@@ -194,32 +232,44 @@ public final class HttpFederatedProtocol {
this.mAssignmentId = taskAssignment.getAssignmentId();
}
- private ListenableFuture<CheckinResult> getCheckinResult(
+ private CheckinResult getCheckinResult(
ListenableFuture<FederatedComputeHttpResponse> planDataResponseFuture,
ListenableFuture<FederatedComputeHttpResponse> checkpointDataResponseFuture,
- ListenableFuture<TaskAssignment> taskAssignmentFuture) {
+ TaskAssignment taskAssignment)
+ throws Exception {
+
+ FederatedComputeHttpResponse planDataResponse = Futures.getDone(planDataResponseFuture);
+ FederatedComputeHttpResponse checkpointDataResponse =
+ Futures.getDone(checkpointDataResponseFuture);
+ validateHttpResponseStatus("Fetch plan", planDataResponse);
+ validateHttpResponseStatus("Fetch checkpoint", checkpointDataResponse);
+
+ // Process download ClientOnlyPlan.
+ byte[] planData = planDataResponse.getPayload();
+ if (taskAssignment.getPlan().getCompressionFormat()
+ == ResourceCompressionFormat.RESOURCE_COMPRESSION_FORMAT_GZIP
+ || planDataResponse.isResponseCompressed()) {
+ planData = uncompressWithGzip(planData);
+ }
+ ClientOnlyPlan clientOnlyPlan;
try {
- FederatedComputeHttpResponse planDataResponse = Futures.getDone(planDataResponseFuture);
- FederatedComputeHttpResponse checkpointDataResponse =
- Futures.getDone(checkpointDataResponseFuture);
- TaskAssignment taskAssignment = Futures.getDone(taskAssignmentFuture);
- validateHttpResponseStatus("Fetch plan", planDataResponse);
- validateHttpResponseStatus("Fetch checkpoint", checkpointDataResponse);
- ClientOnlyPlan clientOnlyPlan;
- try {
- clientOnlyPlan = ClientOnlyPlan.parseFrom(planDataResponse.getPayload());
- } catch (InvalidProtocolBufferException e) {
- LogUtil.e(TAG, e, "Could not parse ClientOnlyPlan proto");
- return Futures.immediateFailedFuture(
- new IllegalStateException("Could not parse ClientOnlyPlan proto", e));
- }
- String inputCheckpointFile = createTempFile("input", ".ckp");
- writeToFile(inputCheckpointFile, checkpointDataResponse.getPayload());
- return Futures.immediateFuture(
- new CheckinResult(inputCheckpointFile, clientOnlyPlan, taskAssignment));
- } catch (Exception e) {
- return Futures.immediateFailedFuture(e);
+ clientOnlyPlan = ClientOnlyPlan.parseFrom(planData);
+ } catch (InvalidProtocolBufferException e) {
+ LogUtil.e(TAG, e, "Could not parse ClientOnlyPlan proto");
+ throw new IllegalStateException("Could not parse ClientOnlyPlan proto", e);
}
+
+ // Process download checkpoint resource.
+ String inputCheckpointFile = createTempFile("input", ".ckp");
+ byte[] checkpointData = checkpointDataResponse.getPayload();
+ if (taskAssignment.getInitCheckpoint().getCompressionFormat()
+ == ResourceCompressionFormat.RESOURCE_COMPRESSION_FORMAT_GZIP
+ || checkpointDataResponse.isResponseCompressed()) {
+ checkpointData = uncompressWithGzip(checkpointData);
+ }
+ writeToFile(inputCheckpointFile, checkpointData);
+ Trace.endAsyncSection(TRACE_HTTP_ISSUE_CHECKIN, 0);
+ return new CheckinResult(inputCheckpointFile, clientOnlyPlan, taskAssignment);
}
private ListenableFuture<FederatedComputeHttpResponse> performReportResult(
@@ -249,31 +299,26 @@ public final class HttpFederatedProtocol {
HttpMethod.PUT,
startDataUploadRequest.toByteArray(),
/* isProtobufEncoded= */ true);
- return mHttpClient.performRequestAsync(httpRequest);
+ return mHttpClient.performRequestAsyncWithRetry(httpRequest);
}
private ListenableFuture<FederatedComputeHttpResponse>
processReportResultResponseAndUploadResult(
- FederatedComputeHttpResponse httpResponse,
+ ReportResultResponse reportResultResponse,
ComputationResult computationResult) {
try {
- validateHttpResponseStatus("ReportResult", httpResponse);
- ReportResultResponse reportResultResponse =
- ReportResultResponse.parseFrom(httpResponse.getPayload());
- // TODO(b/297605806): better handle rejection info.
- if (reportResultResponse.hasRejectionInfo()) {
- return Futures.immediateFailedFuture(
- new IllegalStateException(
- "ReportResult got rejection: " + httpResponse.getStatusCode()));
- }
Preconditions.checkArgument(
!computationResult.getOutputCheckpointFile().isEmpty(),
"Output checkpoint file should not be empty");
- byte[] outputBytes = readFileAsByteArray(computationResult.getOutputCheckpointFile());
UploadInstruction uploadInstruction = reportResultResponse.getUploadInstruction();
Preconditions.checkArgument(
!uploadInstruction.getUploadLocation().isEmpty(),
"UploadInstruction.upload_location must not be empty");
+ byte[] outputBytes = readFileAsByteArray(computationResult.getOutputCheckpointFile());
+ if (uploadInstruction.getCompressionFormat()
+ == ResourceCompressionFormat.RESOURCE_COMPRESSION_FORMAT_GZIP) {
+ outputBytes = compressWithGzip(outputBytes);
+ }
HashMap<String, String> requestHeader = new HashMap<>();
uploadInstruction
.getExtraRequestHeadersMap()
@@ -293,14 +338,21 @@ public final class HttpFederatedProtocol {
uploadInstruction.getUploadLocation(),
HttpMethod.PUT,
requestHeader,
- outputBytes,
- /* useCompression= */ false);
- return mHttpClient.performRequestAsync(httpUploadRequest);
+ outputBytes);
+ return mHttpClient.performRequestAsyncWithRetry(httpUploadRequest);
} catch (Exception e) {
return Futures.immediateFailedFuture(e);
}
}
+ private ReportResultResponse getReportResultResponse(FederatedComputeHttpResponse httpResponse)
+ throws InvalidProtocolBufferException {
+ validateHttpResponseStatus("ReportResult", httpResponse);
+ ReportResultResponse reportResultResponse =
+ ReportResultResponse.parseFrom(httpResponse.getPayload());
+ return reportResultResponse;
+ }
+
private void validateHttpResponseStatus(
String stage, FederatedComputeHttpResponse httpResponse) {
if (!HTTP_OK_STATUS.contains(httpResponse.getStatusCode())) {
@@ -315,14 +367,22 @@ public final class HttpFederatedProtocol {
case URI:
Preconditions.checkArgument(
!resource.getUri().isEmpty(), "Resource.uri must be non-empty when set");
+ HashMap<String, String> headerList = new HashMap<>();
+ if (resource.getCompressionFormat()
+ == ResourceCompressionFormat.RESOURCE_COMPRESSION_FORMAT_GZIP) {
+ // Set this header to disable decompressive transcoding when download from
+ // Google Cloud Storage.
+ // https://cloud.google.com/storage/docs/transcoding#decompressive_transcoding
+ headerList.put(ACCEPT_ENCODING_HDR, GZIP_ENCODING_HDR);
+ }
+ LogUtil.d(TAG, "start fetch task resources");
FederatedComputeHttpRequest httpRequest =
FederatedComputeHttpRequest.create(
resource.getUri(),
HttpMethod.GET,
- new HashMap<String, String>(),
- HttpClientUtil.EMPTY_BODY,
- /* useCompression= */ false);
- return mHttpClient.performRequestAsync(httpRequest);
+ headerList,
+ HttpClientUtil.EMPTY_BODY);
+ return mHttpClient.performRequestAsyncWithRetry(httpRequest);
case INLINE_RESOURCE:
return Futures.immediateFailedFuture(
new UnsupportedOperationException("Inline resource is not supported yet."));
diff --git a/federatedcompute/src/com/android/federatedcompute/services/http/ProtocolRequestCreator.java b/federatedcompute/src/com/android/federatedcompute/services/http/ProtocolRequestCreator.java
index e87447f4..d73f1d05 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/http/ProtocolRequestCreator.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/http/ProtocolRequestCreator.java
@@ -32,28 +32,23 @@ import java.util.HashMap;
public final class ProtocolRequestCreator {
private final String mRequestBaseUri;
private final HashMap<String, String> mHeaderList;
- private boolean mUseCompression;
- public ProtocolRequestCreator(
- String requestBaseUri, HashMap<String, String> headerList, boolean useCompression) {
+ public ProtocolRequestCreator(String requestBaseUri, HashMap<String, String> headerList) {
this.mRequestBaseUri = requestBaseUri;
this.mHeaderList = headerList;
- this.mUseCompression = useCompression;
}
/**
* Creates a {@link ProtocolRequestCreator} based on forwarding info. Validates and extracts the
* base URI for the subsequent requests.
*/
- public static ProtocolRequestCreator create(
- ForwardingInfo forwardingInfo, boolean useCompression) {
+ public static ProtocolRequestCreator create(ForwardingInfo forwardingInfo) {
if (forwardingInfo.getTargetUriPrefix().isEmpty()) {
throw new IllegalArgumentException("Missing `ForwardingInfo.target_uri_prefix`");
}
HashMap<String, String> extraHeaders = new HashMap<>();
extraHeaders.putAll(forwardingInfo.getExtraRequestHeadersMap());
- return new ProtocolRequestCreator(
- forwardingInfo.getTargetUriPrefix(), extraHeaders, useCompression);
+ return new ProtocolRequestCreator(forwardingInfo.getTargetUriPrefix(), extraHeaders);
}
/** Creates a {@link FederatedComputeHttpRequest} with base uri and compression setting. */
@@ -70,30 +65,21 @@ public final class ProtocolRequestCreator {
public FederatedComputeHttpRequest createProtoRequest(
String uri,
HttpMethod httpMethod,
- HashMap<String, String> params,
+ HashMap<String, String> extraHeaders,
byte[] requestBody,
boolean isProtobufEncoded) {
HashMap<String, String> requestHeader = new HashMap<>();
requestHeader.putAll(mHeaderList);
+ requestHeader.putAll(extraHeaders);
if (isProtobufEncoded && requestBody.length > 0) {
requestHeader.put(CONTENT_TYPE_HDR, PROTOBUF_CONTENT_TYPE);
}
-
- String requestUriSuffix = uri;
- if (!params.isEmpty()) {
- requestUriSuffix = uri.concat("?");
- for (String key : params.keySet()) {
- requestUriSuffix = requestUriSuffix.concat(String.join("=", key, params.get(key)));
- }
- }
-
return FederatedComputeHttpRequest.create(
- joinBaseUriWithSuffix(mRequestBaseUri, requestUriSuffix),
+ joinBaseUriWithSuffix(mRequestBaseUri, uri),
httpMethod,
requestHeader,
- requestBody,
- mUseCompression);
+ requestBody);
}
private String joinBaseUriWithSuffix(String baseUri, String suffix) {
diff --git a/federatedcompute/src/com/android/federatedcompute/services/scheduling/FederatedComputeJobManager.java b/federatedcompute/src/com/android/federatedcompute/services/scheduling/FederatedComputeJobManager.java
index cfd38970..eb141c65 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/scheduling/FederatedComputeJobManager.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/scheduling/FederatedComputeJobManager.java
@@ -99,6 +99,52 @@ public class FederatedComputeJobManager {
return sSingletonInstance;
}
+ /** We enforce device idle, battery not low and unmetered network training constraints. */
+ private static byte[] buildTrainingConstraints() {
+ FlatBufferBuilder builder = new FlatBufferBuilder();
+ builder.finish(
+ TrainingConstraints.createTrainingConstraints(
+ builder,
+ /** requiresSchedulerIdle= */
+ true,
+ /** requiresSchedulerBatteryNotLow= */
+ true,
+ /** requiresSchedulerUnmeteredNetwork= */
+ true));
+ return builder.sizedByteArray();
+ }
+
+ private static byte[] buildDefaultTrainingInterval() {
+ FlatBufferBuilder builder = new FlatBufferBuilder();
+ builder.finish(
+ TrainingIntervalOptions.createTrainingIntervalOptions(
+ builder, SchedulingMode.ONE_TIME, 0));
+ return builder.sizedByteArray();
+ }
+
+ private static byte[] buildTrainingIntervalOptions(
+ @Nullable TrainingInterval trainingInterval) {
+ if (trainingInterval == null) {
+ return buildDefaultTrainingInterval();
+ }
+
+ FlatBufferBuilder builder = new FlatBufferBuilder();
+ builder.finish(
+ TrainingIntervalOptions.createTrainingIntervalOptions(
+ builder,
+ convertSchedulingMode(trainingInterval.getSchedulingMode()),
+ trainingInterval.getMinimumIntervalMillis()));
+
+ return builder.sizedByteArray();
+ }
+
+ private static boolean trainingIntervalChanged(
+ TrainingOptions newTaskOptions, FederatedTrainingTask existingTask) {
+ byte[] incomingTrainingIntervalOptions =
+ buildTrainingIntervalOptions(newTaskOptions.getTrainingInterval());
+ return !Arrays.equals(incomingTrainingIntervalOptions, existingTask.intervalOptions());
+ }
+
/**
* Called when a client indicates via the client API that a task with the given parameters
* should be scheduled.
@@ -327,45 +373,6 @@ public class FederatedComputeJobManager {
return mJobSchedulerHelper.scheduleTask(mContext, newTask);
}
- /** We enforce device idle, battery not low and unmetered network training constraints. */
- private static byte[] buildTrainingConstraints() {
- FlatBufferBuilder builder = new FlatBufferBuilder();
- builder.finish(
- TrainingConstraints.createTrainingConstraints(
- builder,
- /** requiresSchedulerIdle= */
- true,
- /** requiresSchedulerBatteryNotLow= */
- true,
- /** requiresSchedulerUnmeteredNetwork= */
- true));
- return builder.sizedByteArray();
- }
-
- private static byte[] buildDefaultTrainingInterval() {
- FlatBufferBuilder builder = new FlatBufferBuilder();
- builder.finish(
- TrainingIntervalOptions.createTrainingIntervalOptions(
- builder, SchedulingMode.ONE_TIME, 0));
- return builder.sizedByteArray();
- }
-
- private static byte[] buildTrainingIntervalOptions(
- @Nullable TrainingInterval trainingInterval) {
- if (trainingInterval == null) {
- return buildDefaultTrainingInterval();
- }
-
- FlatBufferBuilder builder = new FlatBufferBuilder();
- builder.finish(
- TrainingIntervalOptions.createTrainingIntervalOptions(
- builder,
- convertSchedulingMode(trainingInterval.getSchedulingMode()),
- trainingInterval.getMinimumIntervalMillis()));
-
- return builder.sizedByteArray();
- }
-
private boolean detectKeyParametersChanged(
TrainingOptions newTaskOptions, FederatedTrainingTask existingTask) {
// Check if the task previously had a different population name.
@@ -389,11 +396,4 @@ public class FederatedComputeJobManager {
}
return populationChanged || trainingIntervalChanged;
}
-
- private static boolean trainingIntervalChanged(
- TrainingOptions newTaskOptions, FederatedTrainingTask existingTask) {
- byte[] incomingTrainingIntervalOptions =
- buildTrainingIntervalOptions(newTaskOptions.getTrainingInterval());
- return !Arrays.equals(incomingTrainingIntervalOptions, existingTask.intervalOptions());
- }
}
diff --git a/federatedcompute/src/com/android/federatedcompute/services/training/FederatedComputeWorker.java b/federatedcompute/src/com/android/federatedcompute/services/training/FederatedComputeWorker.java
index c991b916..4244aff4 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/training/FederatedComputeWorker.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/training/FederatedComputeWorker.java
@@ -18,6 +18,8 @@ package com.android.federatedcompute.services.training;
import static com.android.federatedcompute.services.common.Constants.CLIENT_ONLY_PLAN_FILE_NAME;
import static com.android.federatedcompute.services.common.Constants.ISOLATED_TRAINING_SERVICE_NAME;
+import static com.android.federatedcompute.services.common.Constants.TRACE_WORKER_RUN_FL_COMPUTATION;
+import static com.android.federatedcompute.services.common.Constants.TRACE_WORKER_START_TRAINING_RUN;
import static com.android.federatedcompute.services.common.FederatedComputeExecutors.getBackgroundExecutor;
import static com.android.federatedcompute.services.common.FederatedComputeExecutors.getLightweightExecutor;
import static com.android.federatedcompute.services.common.FileUtils.createTempFile;
@@ -33,6 +35,7 @@ import android.federatedcompute.common.ClientConstants;
import android.federatedcompute.common.ExampleConsumption;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.os.Trace;
import androidx.concurrent.futures.CallbackToFutureAdapter;
@@ -57,6 +60,7 @@ import com.android.internal.util.Preconditions;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
@@ -66,6 +70,9 @@ import com.google.intelligence.fcp.client.RetryInfo;
import com.google.intelligence.fcp.client.engine.TaskRetry;
import com.google.internal.federated.plan.ClientOnlyPlan;
import com.google.internal.federated.plan.ExampleSelector;
+import com.google.internal.federatedcompute.v1.RejectionInfo;
+import com.google.internal.federatedcompute.v1.RetryWindow;
+import com.google.protobuf.Duration;
import com.google.protobuf.InvalidProtocolBufferException;
import java.io.IOException;
@@ -157,6 +164,7 @@ public class FederatedComputeWorker {
int jobId, FederatedTrainingTask trainingTask) {
synchronized (mLock) {
// Only allows one concurrent job running.
+ Trace.beginAsyncSection(TRACE_WORKER_START_TRAINING_RUN, jobId);
TrainingRun run = new TrainingRun(jobId, trainingTask);
mActiveRun = run;
ListenableFuture<FLRunnerResult> runCompletedFuture = doTraining(run);
@@ -165,6 +173,8 @@ public class FederatedComputeWorker {
.call(
() -> {
unBindServicesIfNecessary(run);
+ Trace.endAsyncSection(
+ TRACE_WORKER_START_TRAINING_RUN, jobId);
return null;
},
mInjector.getBgExecutor());
@@ -220,77 +230,170 @@ public class FederatedComputeWorker {
ListenableFuture<CheckinResult> checkinResultFuture =
mHttpFederatedProtocol.issueCheckin();
- // 2. Bind to client app implemented ExampleStoreService based on ExampleSelector.
- ListenableFuture<IExampleStoreIterator> iteratorFuture =
- FluentFuture.from(checkinResultFuture)
- .transform(
- result -> {
- // Set active run's task name.
- String taskName = result.getTaskAssignment().getTaskName();
- Preconditions.checkArgument(
- !taskName.isEmpty(),
- "Task name should not be empty");
- synchronized (mLock) {
- mActiveRun.mTaskName = taskName;
- }
- return getExampleSelector(result);
- },
- getLightweightExecutor())
- .transformAsync(
- selector ->
- getExampleStoreIterator(
- run,
- run.mTask.appPackageName(),
- run.mTaskName,
- selector),
- mInjector.getBgExecutor());
-
- // 3. Run federated learning or federated analytic depends on task type. Federated
- // learning job will start a new isolated process to run TFLite training.
- ListenableFuture<ComputationResult> computationResultFuture =
- Futures.whenAllSucceed(checkinResultFuture, iteratorFuture)
- .callAsync(
- () ->
- runFederatedComputation(
- Futures.getDone(checkinResultFuture),
- run,
- Futures.getDone(iteratorFuture)),
- mInjector.getBgExecutor());
-
- // 4. Report computation result to federated compute server.
- ListenableFuture<Void> reportToServerFuture =
- FluentFuture.from(computationResultFuture)
- .transformAsync(
- result -> mHttpFederatedProtocol.reportResult(result),
- getLightweightExecutor());
- return FluentFuture.from(
- Futures.whenAllSucceed(reportToServerFuture, computationResultFuture)
- .call(
- () -> {
- ComputationResult result =
- Futures.getDone(computationResultFuture);
- var reportToServer = Futures.getDone(reportToServerFuture);
- // 5. Publish computation result and consumed
- // examples to client implemented
- // ResultHandlingService.
- var unused =
- mResultCallbackHelper.callHandleResult(
- run.mTaskName, run.mTask, result);
- return result.getFlRunnerResult();
- },
- mInjector.getBgExecutor()));
+ return FluentFuture.from(checkinResultFuture)
+ .transformAsync(
+ checkinResult -> processCheckinAndDoFlTraining(run, checkinResult),
+ mInjector.getBgExecutor());
} catch (Exception e) {
return Futures.immediateFailedFuture(e);
}
}
+ @androidx.annotation.NonNull
+ private ListenableFuture<FLRunnerResult> processCheckinAndDoFlTraining(
+ TrainingRun run, CheckinResult checkinResult) {
+ // Stop processing if have rejection Info
+ if (checkinResult.getRejectionInfo() != null) {
+ mJobManager.onTrainingCompleted(
+ run.mTask.jobId(),
+ run.mTask.populationName(),
+ run.mTask.getTrainingIntervalOptions(),
+ buildTaskRetry(checkinResult.getRejectionInfo()),
+ ContributionResult.FAIL);
+ return Futures.immediateFuture(null);
+ }
+ // 2. Bind to client app implemented ExampleStoreService based on ExampleSelector.
+ // Set active run's task name.
+ String taskName = checkinResult.getTaskAssignment().getTaskName();
+ Preconditions.checkArgument(!taskName.isEmpty(), "Task name should not be empty");
+ synchronized (mLock) {
+ mActiveRun.mTaskName = taskName;
+ }
+ ListenableFuture<IExampleStoreIterator> iteratorFuture =
+ getExampleStoreIterator(
+ run,
+ run.mTask.appPackageName(),
+ run.mTaskName,
+ getExampleSelector(checkinResult));
+ // report failure to server if getting iterator failed with any exception.
+ FutureCallback<Object> serverFailureReportCallback = getServerFailureReportCallback();
+ Futures.addCallback(
+ iteratorFuture, serverFailureReportCallback, getLightweightExecutor());
+
+ // 3. Run federated learning or federated analytic depends on task type. Federated
+ // learning job will start a new isolated process to run TFLite training.
+ FluentFuture<ComputationResult> computationResultFuture =
+ FluentFuture.from(iteratorFuture)
+ .transformAsync(
+ iterator -> runFederatedComputation(checkinResult, run, iterator),
+ mInjector.getBgExecutor());
+ // report failure to server if computation failed with any exception.
+ computationResultFuture.addCallback(
+ serverFailureReportCallback, getLightweightExecutor());
+
+ // 4. Report computation result to federated compute server.
+ ListenableFuture<RejectionInfo> reportToServerFuture =
+ computationResultFuture.transformAsync(
+ result -> mHttpFederatedProtocol.reportResult(result),
+ getLightweightExecutor());
+ return Futures.whenAllSucceed(reportToServerFuture, computationResultFuture)
+ .call(
+ () -> {
+ ComputationResult computationResult =
+ Futures.getDone(computationResultFuture);
+ RejectionInfo reportToServer = Futures.getDone(reportToServerFuture);
+ // report to Server will hold null in case of success, or rejection info
+ // in case server answered with rejection
+ if (reportToServer != null) {
+ ComputationResult failedReportComputationResult =
+ new ComputationResult(
+ null,
+ FLRunnerResult.newBuilder()
+ .mergeFrom(
+ computationResult
+ .getFlRunnerResult())
+ .setContributionResult(
+ ContributionResult.FAIL)
+ .build(),
+ null);
+ var unused =
+ mResultCallbackHelper.callHandleResult(
+ run.mTaskName,
+ run.mTask,
+ failedReportComputationResult);
+ mJobManager.onTrainingCompleted(
+ run.mTask.jobId(),
+ run.mTask.populationName(),
+ run.mTask.getTrainingIntervalOptions(),
+ buildTaskRetry(reportToServer),
+ ContributionResult.FAIL);
+ return null;
+ }
+ // 5. Publish computation result and consumed
+ // examples to client implemented
+ // ResultHandlingService.
+ var unused =
+ mResultCallbackHelper.callHandleResult(
+ run.mTaskName, run.mTask, computationResult);
+ return computationResult.getFlRunnerResult();
+ },
+ mInjector.getBgExecutor());
+ }
+
+ @androidx.annotation.NonNull
+ private FutureCallback<Object> getServerFailureReportCallback() {
+ return new FutureCallback<Object>() {
+ volatile int mNumberOfInvocations = 0;
+
+ @Override
+ public void onSuccess(Object unused) {
+ // do nothing.
+ }
+
+ // We do not want race condition and repeating reporting failures from computation
+ // failed future in right before case Example Store iterator failed.
+ // Thus method is synchronised.
+ @Override
+ public synchronized void onFailure(Throwable throwable) {
+ if (mNumberOfInvocations < 1) {
+ LogUtil.d(
+ TAG,
+ "Training failed. Reporting failure result to server due to exception.",
+ throwable);
+ ComputationResult failedReportComputationResult =
+ new ComputationResult(
+ null,
+ FLRunnerResult.newBuilder()
+ .setContributionResult(ContributionResult.FAIL)
+ .setErrorMessage(throwable.getMessage())
+ .build(),
+ null);
+ var unused = mHttpFederatedProtocol.reportResult(failedReportComputationResult);
+ }
+ mNumberOfInvocations++;
+ }
+ };
+ }
+
+ private static TaskRetry buildTaskRetry(RejectionInfo rejectionInfo) {
+ TaskRetry.Builder taskRetryBuilder =
+ TaskRetry.newBuilder();
+ if (rejectionInfo.hasRetryWindow()) {
+ RetryWindow retryWindow =
+ rejectionInfo.getRetryWindow();
+ Duration delayMin = retryWindow.getDelayMin();
+ // convert rejection info seconds and nanoseconds to
+ // retry milliseconds
+ taskRetryBuilder.setDelayMin(
+ delayMin.getSeconds() * 1000
+ + delayMin.getNanos() / 1000000);
+ Duration delayMax = retryWindow.getDelayMax();
+ taskRetryBuilder.setDelayMax(
+ delayMax.getSeconds() * 1000
+ + delayMax.getNanos() / 1000000);
+ }
+ return taskRetryBuilder.build();
+ }
+
/**
* Completes the running job , schedule recurrent job, and unbind from ExampleStoreService and
* ResultHandlingService etc.
*/
public void finish(FLRunnerResult flRunnerResult) {
TaskRetry taskRetry = null;
+ ContributionResult contributionResult = ContributionResult.UNSPECIFIED;
if (flRunnerResult != null) {
+ contributionResult = flRunnerResult.getContributionResult();
if (flRunnerResult.hasRetryInfo()) {
RetryInfo retryInfo = flRunnerResult.getRetryInfo();
long delay = retryInfo.getMinimumDelay().getSeconds() * 1000L;
@@ -303,7 +406,7 @@ public class FederatedComputeWorker {
LogUtil.i(TAG, "Finished with task retry= %s", taskRetry);
}
}
- finish(taskRetry, flRunnerResult.getContributionResult(), true);
+ finish(taskRetry, contributionResult, true);
}
/**
@@ -393,6 +496,7 @@ public class FederatedComputeWorker {
CheckinResult checkinResult,
String outputCheckpointFile,
IExampleStoreIterator iterator) {
+ Trace.beginAsyncSection(TRACE_WORKER_RUN_FL_COMPUTATION, 0);
ParcelFileDescriptor outputCheckpointFd =
createTempFileDescriptor(
outputCheckpointFile, ParcelFileDescriptor.MODE_READ_WRITE);
@@ -458,6 +562,7 @@ public class FederatedComputeWorker {
unbindFromIsolatedTrainingService();
run.mIsolatedTrainingService = null;
}
+ Trace.endAsyncSection(TRACE_WORKER_RUN_FL_COMPUTATION, 0);
return computationResult;
},
getLightweightExecutor());
diff --git a/federatedcompute/src/com/android/federatedcompute/services/training/IsolatedTrainingServiceImpl.java b/federatedcompute/src/com/android/federatedcompute/services/training/IsolatedTrainingServiceImpl.java
index ebaf7ef0..79966d99 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/training/IsolatedTrainingServiceImpl.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/training/IsolatedTrainingServiceImpl.java
@@ -16,6 +16,8 @@
package com.android.federatedcompute.services.training;
+import static com.android.federatedcompute.services.common.Constants.TRACE_ISOLATED_PROCESS_RUN_FL_TRAINING;
+
import android.annotation.NonNull;
import android.federatedcompute.aidl.IExampleStoreIterator;
import android.federatedcompute.common.ClientConstants;
@@ -23,6 +25,7 @@ import android.federatedcompute.common.ExampleConsumption;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.Trace;
import com.android.federatedcompute.internal.util.LogUtil;
import com.android.federatedcompute.services.common.Constants;
@@ -70,6 +73,7 @@ public class IsolatedTrainingServiceImpl extends IIsolatedTrainingService.Stub {
public void runFlTraining(@NonNull Bundle params, @NonNull ITrainingResultCallback callback) {
Objects.requireNonNull(params);
Objects.requireNonNull(callback);
+ Trace.beginAsyncSection(TRACE_ISOLATED_PROCESS_RUN_FL_TRAINING, 0);
IExampleStoreIterator exampleStoreIteratorBinder =
IExampleStoreIterator.Stub.asInterface(
@@ -135,16 +139,21 @@ public class IsolatedTrainingServiceImpl extends IIsolatedTrainingService.Stub {
bundle.putByteArray(Constants.EXTRA_FL_RUNNER_RESULT, result.toByteArray());
ArrayList<ExampleConsumption> exampleConsumptionArrayList =
recorder.finishRecordingAndGet();
+ int numExamples = 0;
+ for (ExampleConsumption exampleConsumption : exampleConsumptionArrayList) {
+ numExamples += exampleConsumption.getExampleCount();
+ }
LogUtil.i(
TAG,
"training task %s: result %s, used %d examples",
populationName,
result.toString(),
- exampleConsumptionArrayList.size());
+ numExamples);
bundle.putParcelableArrayList(
ClientConstants.EXTRA_EXAMPLE_CONSUMPTION_LIST,
exampleConsumptionArrayList);
sendResult(bundle, callback);
+ Trace.endAsyncSection(TRACE_ISOLATED_PROCESS_RUN_FL_TRAINING, 0);
}
@Override
diff --git a/federatedcompute/src/com/android/federatedcompute/services/training/jni/FlRunnerWrapper.java b/federatedcompute/src/com/android/federatedcompute/services/training/jni/FlRunnerWrapper.java
index 33ec656f..30fa4ce1 100644
--- a/federatedcompute/src/com/android/federatedcompute/services/training/jni/FlRunnerWrapper.java
+++ b/federatedcompute/src/com/android/federatedcompute/services/training/jni/FlRunnerWrapper.java
@@ -16,6 +16,10 @@
package com.android.federatedcompute.services.training.jni;
+import static com.android.federatedcompute.services.common.Constants.TRACE_NATIVE_RUN_FEDERATED_COMPUTATION;
+
+import android.os.Trace;
+
import com.android.federatedcompute.internal.util.LogUtil;
import com.android.federatedcompute.services.examplestore.ExampleIterator;
import com.android.federatedcompute.services.training.util.ListenableSupplier;
@@ -57,6 +61,7 @@ public final class FlRunnerWrapper implements Closeable {
ClientOnlyPlan clientOnlyPlan,
String checkpointInputFileName,
String checkpointOutputFileName) {
+ Trace.beginAsyncSection(TRACE_NATIVE_RUN_FEDERATED_COMPUTATION, 0);
SimpleTaskEnvironmentImpl simpleTaskEnv =
new SimpleTaskEnvironmentImpl(mInterruptionFlag, mExampleIterator);
byte[] flRunnerResultSerialized =
@@ -77,6 +82,8 @@ public final class FlRunnerWrapper implements Closeable {
// recover from it.
LogUtil.e(TAG, "Cannot parse FLRunnerResult", e);
throw new IllegalArgumentException(e);
+ } finally {
+ Trace.endAsyncSection(TRACE_NATIVE_RUN_FEDERATED_COMPUTATION, 0);
}
}
diff --git a/framework/api/current.txt b/framework/api/current.txt
index e07681c6..87e868ae 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -12,11 +12,8 @@ package android.adservices.ondevicepersonalization {
method @NonNull public java.util.Map<java.lang.String,byte[]> getData();
}
- @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class DownloadCompletedOutput implements android.os.Parcelable {
- method public int describeContents();
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class DownloadCompletedOutput {
method @NonNull public java.util.List<java.lang.String> getRetainedKeys();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.adservices.ondevicepersonalization.DownloadCompletedOutput> CREATOR;
}
@FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public static final class DownloadCompletedOutput.Builder {
@@ -26,10 +23,17 @@ package android.adservices.ondevicepersonalization {
method @NonNull public android.adservices.ondevicepersonalization.DownloadCompletedOutput.Builder setRetainedKeys(@NonNull java.util.List<java.lang.String>);
}
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class EventInput {
+ method @NonNull public android.os.PersistableBundle getParameters();
+ method @Nullable public android.adservices.ondevicepersonalization.RequestLogRecord getRequestLogRecord();
+ }
+
@FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class EventLogRecord implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.content.ContentValues getData();
+ method @Nullable public android.adservices.ondevicepersonalization.RequestLogRecord getRequestLogRecord();
method @IntRange(from=0) public int getRowIndex();
+ method @NonNull public java.time.Instant getTime();
method @IntRange(from=1, to=127) public int getType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.adservices.ondevicepersonalization.EventLogRecord> CREATOR;
@@ -39,29 +43,34 @@ package android.adservices.ondevicepersonalization {
ctor public EventLogRecord.Builder();
method @NonNull public android.adservices.ondevicepersonalization.EventLogRecord build();
method @NonNull public android.adservices.ondevicepersonalization.EventLogRecord.Builder setData(@NonNull android.content.ContentValues);
+ method @NonNull public android.adservices.ondevicepersonalization.EventLogRecord.Builder setRequestLogRecord(@NonNull android.adservices.ondevicepersonalization.RequestLogRecord);
method @NonNull public android.adservices.ondevicepersonalization.EventLogRecord.Builder setRowIndex(@IntRange(from=0) int);
method @NonNull public android.adservices.ondevicepersonalization.EventLogRecord.Builder setType(@IntRange(from=1, to=127) int);
}
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class EventOutput {
+ method @Nullable public android.adservices.ondevicepersonalization.EventLogRecord getEventLogRecord();
+ }
+
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public static final class EventOutput.Builder {
+ ctor public EventOutput.Builder();
+ method @NonNull public android.adservices.ondevicepersonalization.EventOutput build();
+ method @NonNull public android.adservices.ondevicepersonalization.EventOutput.Builder setEventLogRecord(@NonNull android.adservices.ondevicepersonalization.EventLogRecord);
+ }
+
@FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public class EventUrlProvider {
method @NonNull @WorkerThread public android.net.Uri createEventTrackingUrlWithRedirect(@NonNull android.os.PersistableBundle, @Nullable android.net.Uri);
method @NonNull @WorkerThread public android.net.Uri createEventTrackingUrlWithResponse(@NonNull android.os.PersistableBundle, @Nullable byte[], @Nullable String);
}
- @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class ExecuteInput implements android.os.Parcelable {
- method public int describeContents();
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class ExecuteInput {
method @NonNull public String getAppPackageName();
method @NonNull public android.os.PersistableBundle getAppParams();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.adservices.ondevicepersonalization.ExecuteInput> CREATOR;
}
- @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class ExecuteOutput implements android.os.Parcelable {
- method public int describeContents();
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class ExecuteOutput {
method @NonNull public java.util.List<android.adservices.ondevicepersonalization.RenderingConfig> getRenderingConfigs();
method @Nullable public android.adservices.ondevicepersonalization.RequestLogRecord getRequestLogRecord();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.adservices.ondevicepersonalization.ExecuteOutput> CREATOR;
}
@FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public static final class ExecuteOutput.Builder {
@@ -72,10 +81,32 @@ package android.adservices.ondevicepersonalization {
method @NonNull public android.adservices.ondevicepersonalization.ExecuteOutput.Builder setRequestLogRecord(@NonNull android.adservices.ondevicepersonalization.RequestLogRecord);
}
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class FederatedComputeInput {
+ method @NonNull public String getPopulationName();
+ }
+
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public static final class FederatedComputeInput.Builder {
+ ctor public FederatedComputeInput.Builder();
+ method @NonNull public android.adservices.ondevicepersonalization.FederatedComputeInput build();
+ method @NonNull public android.adservices.ondevicepersonalization.FederatedComputeInput.Builder setPopulationName(@NonNull String);
+ }
+
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public class FederatedComputeScheduler {
+ method @WorkerThread public void cancel(@NonNull String);
+ method @WorkerThread public void schedule(@NonNull android.adservices.ondevicepersonalization.FederatedComputeScheduler.Params, @NonNull android.adservices.ondevicepersonalization.FederatedComputeInput);
+ }
+
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public static class FederatedComputeScheduler.Params {
+ ctor public FederatedComputeScheduler.Params(@NonNull android.adservices.ondevicepersonalization.TrainingInterval);
+ method @NonNull public android.adservices.ondevicepersonalization.TrainingInterval getTrainingInterval();
+ }
+
@FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public abstract class IsolatedService extends android.app.Service {
ctor public IsolatedService();
method @NonNull public final android.adservices.ondevicepersonalization.EventUrlProvider getEventUrlProvider(@NonNull android.adservices.ondevicepersonalization.RequestToken);
+ method @NonNull public final android.adservices.ondevicepersonalization.FederatedComputeScheduler getFederatedComputeScheduler(@NonNull android.adservices.ondevicepersonalization.RequestToken);
method @NonNull public final android.adservices.ondevicepersonalization.MutableKeyValueStore getLocalData(@NonNull android.adservices.ondevicepersonalization.RequestToken);
+ method @NonNull public final android.adservices.ondevicepersonalization.LogReader getLogReader(@NonNull android.adservices.ondevicepersonalization.RequestToken);
method @NonNull public final android.adservices.ondevicepersonalization.KeyValueStore getRemoteData(@NonNull android.adservices.ondevicepersonalization.RequestToken);
method @Nullable public final android.adservices.ondevicepersonalization.UserData getUserData(@NonNull android.adservices.ondevicepersonalization.RequestToken);
method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
@@ -84,8 +115,10 @@ package android.adservices.ondevicepersonalization {
@FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public interface IsolatedWorker {
method public default void onDownloadCompleted(@NonNull android.adservices.ondevicepersonalization.DownloadCompletedInput, @NonNull java.util.function.Consumer<android.adservices.ondevicepersonalization.DownloadCompletedOutput>);
+ method public default void onEvent(@NonNull android.adservices.ondevicepersonalization.EventInput, @NonNull java.util.function.Consumer<android.adservices.ondevicepersonalization.EventOutput>);
method public default void onExecute(@NonNull android.adservices.ondevicepersonalization.ExecuteInput, @NonNull java.util.function.Consumer<android.adservices.ondevicepersonalization.ExecuteOutput>);
method public default void onRender(@NonNull android.adservices.ondevicepersonalization.RenderInput, @NonNull java.util.function.Consumer<android.adservices.ondevicepersonalization.RenderOutput>);
+ method public default void onTrainingExamples(@NonNull android.adservices.ondevicepersonalization.TrainingExamplesInput, @NonNull java.util.function.Consumer<android.adservices.ondevicepersonalization.TrainingExamplesOutput>);
}
@FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public interface KeyValueStore {
@@ -93,6 +126,11 @@ package android.adservices.ondevicepersonalization {
method @NonNull @WorkerThread public java.util.Set<java.lang.String> keySet();
}
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public class LogReader {
+ method @NonNull @WorkerThread public java.util.List<android.adservices.ondevicepersonalization.EventLogRecord> getJoinedEvents(@NonNull java.time.Instant, @NonNull java.time.Instant);
+ method @NonNull @WorkerThread public java.util.List<android.adservices.ondevicepersonalization.RequestLogRecord> getRequests(@NonNull java.time.Instant, @NonNull java.time.Instant);
+ }
+
@FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public interface MutableKeyValueStore extends android.adservices.ondevicepersonalization.KeyValueStore {
method @Nullable @WorkerThread public byte[] put(@NonNull String, @NonNull byte[]);
method @Nullable @WorkerThread public byte[] remove(@NonNull String);
@@ -104,26 +142,21 @@ package android.adservices.ondevicepersonalization {
}
@FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public class OnDevicePersonalizationManager {
+ method public void execute(@NonNull android.content.ComponentName, @NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.util.List<android.adservices.ondevicepersonalization.SurfacePackageToken>,java.lang.Exception>);
method public void requestSurfacePackage(@NonNull android.adservices.ondevicepersonalization.SurfacePackageToken, @NonNull android.os.IBinder, int, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.view.SurfaceControlViewHost.SurfacePackage,java.lang.Exception>);
}
- @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class RenderInput implements android.os.Parcelable {
- method public int describeContents();
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class RenderInput {
method public int getHeight();
method @Nullable public android.adservices.ondevicepersonalization.RenderingConfig getRenderingConfig();
method public int getRenderingConfigIndex();
method public int getWidth();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.adservices.ondevicepersonalization.RenderInput> CREATOR;
}
- @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class RenderOutput implements android.os.Parcelable {
- method public int describeContents();
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class RenderOutput {
method @Nullable public String getContent();
method @Nullable public String getTemplateId();
method @NonNull public android.os.PersistableBundle getTemplateParams();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.adservices.ondevicepersonalization.RenderOutput> CREATOR;
}
@FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public static final class RenderOutput.Builder {
@@ -151,6 +184,7 @@ package android.adservices.ondevicepersonalization {
@FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class RequestLogRecord implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.content.ContentValues> getRows();
+ method @NonNull public java.time.Instant getTime();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.adservices.ondevicepersonalization.RequestLogRecord> CREATOR;
}
@@ -168,6 +202,40 @@ package android.adservices.ondevicepersonalization {
@FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public class SurfacePackageToken {
}
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class TrainingExamplesInput {
+ method @NonNull public String getPopulationName();
+ method @Nullable public byte[] getResumptionToken();
+ method @NonNull public String getTaskName();
+ }
+
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class TrainingExamplesOutput {
+ method @NonNull public java.util.List<byte[]> getResumptionTokens();
+ method @NonNull public java.util.List<byte[]> getTrainingExamples();
+ }
+
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public static final class TrainingExamplesOutput.Builder {
+ ctor public TrainingExamplesOutput.Builder();
+ method @NonNull public android.adservices.ondevicepersonalization.TrainingExamplesOutput.Builder addResumptionToken(@NonNull byte[]);
+ method @NonNull public android.adservices.ondevicepersonalization.TrainingExamplesOutput.Builder addTrainingExample(@NonNull byte[]);
+ method @NonNull public android.adservices.ondevicepersonalization.TrainingExamplesOutput build();
+ method @NonNull public android.adservices.ondevicepersonalization.TrainingExamplesOutput.Builder setResumptionTokens(@NonNull java.util.List<byte[]>);
+ method @NonNull public android.adservices.ondevicepersonalization.TrainingExamplesOutput.Builder setTrainingExamples(@NonNull java.util.List<byte[]>);
+ }
+
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class TrainingInterval {
+ method @NonNull public java.time.Duration getMinimumInterval();
+ method public int getSchedulingMode();
+ field public static final int SCHEDULING_MODE_ONE_TIME = 1; // 0x1
+ field public static final int SCHEDULING_MODE_RECURRENT = 2; // 0x2
+ }
+
+ @FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public static final class TrainingInterval.Builder {
+ ctor public TrainingInterval.Builder();
+ method @NonNull public android.adservices.ondevicepersonalization.TrainingInterval build();
+ method @NonNull public android.adservices.ondevicepersonalization.TrainingInterval.Builder setMinimumInterval(@NonNull java.time.Duration);
+ method @NonNull public android.adservices.ondevicepersonalization.TrainingInterval.Builder setSchedulingMode(int);
+ }
+
@FlaggedApi(android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS) public final class UserData implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.Map<java.lang.String,android.adservices.ondevicepersonalization.AppInfo> getAppInfos();
diff --git a/framework/java/android/adservices/ondevicepersonalization/DownloadCompletedOutput.aidl b/framework/java/android/adservices/ondevicepersonalization/DownloadCompletedOutput.aidl
deleted file mode 100644
index 8ebe46ef..00000000
--- a/framework/java/android/adservices/ondevicepersonalization/DownloadCompletedOutput.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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;
-
-parcelable DownloadCompletedOutput;
diff --git a/framework/java/android/adservices/ondevicepersonalization/DownloadCompletedOutput.java b/framework/java/android/adservices/ondevicepersonalization/DownloadCompletedOutput.java
index 10d953d0..5c503fcb 100644
--- a/framework/java/android/adservices/ondevicepersonalization/DownloadCompletedOutput.java
+++ b/framework/java/android/adservices/ondevicepersonalization/DownloadCompletedOutput.java
@@ -20,7 +20,6 @@ import static android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ON
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
-import android.os.Parcelable;
import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
import com.android.ondevicepersonalization.internal.util.DataClass;
@@ -35,7 +34,7 @@ import java.util.List;
*/
@FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
@DataClass(genBuilder = true, genEqualsHashCode = true)
-public final class DownloadCompletedOutput implements Parcelable {
+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.
@@ -104,50 +103,6 @@ public final class DownloadCompletedOutput implements Parcelable {
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(mRetainedKeys);
- }
-
- @Override
- @DataClass.Generated.Member
- public int describeContents() { return 0; }
-
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- @DataClass.Generated.Member
- /* package-private */ DownloadCompletedOutput(@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<DownloadCompletedOutput> CREATOR
- = new Parcelable.Creator<DownloadCompletedOutput>() {
- @Override
- public DownloadCompletedOutput[] newArray(int size) {
- return new DownloadCompletedOutput[size];
- }
-
- @Override
- public DownloadCompletedOutput createFromParcel(@NonNull android.os.Parcel in) {
- return new DownloadCompletedOutput(in);
- }
- };
-
/**
* A builder for {@link DownloadCompletedOutput}
*/
@@ -205,10 +160,10 @@ public final class DownloadCompletedOutput implements Parcelable {
}
@DataClass.Generated(
- time = 1696972554365L,
+ 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 [android.os.Parcelable]\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)")
+ 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 []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/framework/java/android/adservices/ondevicepersonalization/DownloadCompletedOutputParcel.java b/framework/java/android/adservices/ondevicepersonalization/DownloadCompletedOutputParcel.java
new file mode 100644
index 00000000..7a906f1d
--- /dev/null
+++ b/framework/java/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]\n@com.android.ondevicepersonalization.internal.util.DataClass(genAidl=false, genBuilder=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/framework/java/android/adservices/ondevicepersonalization/EventInput.aidl b/framework/java/android/adservices/ondevicepersonalization/EventInput.aidl
deleted file mode 100644
index 156f47e7..00000000
--- a/framework/java/android/adservices/ondevicepersonalization/EventInput.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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;
-
-parcelable EventInput;
diff --git a/framework/java/android/adservices/ondevicepersonalization/EventInput.java b/framework/java/android/adservices/ondevicepersonalization/EventInput.java
index 8540aaf3..33057ad6 100644
--- a/framework/java/android/adservices/ondevicepersonalization/EventInput.java
+++ b/framework/java/android/adservices/ondevicepersonalization/EventInput.java
@@ -16,9 +16,11 @@
package android.adservices.ondevicepersonalization;
+import static android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.Parcelable;
import android.os.PersistableBundle;
import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
@@ -27,10 +29,10 @@ import com.android.ondevicepersonalization.internal.util.DataClass;
/**
* The input data for {@link
* IsolatedWorker#onEvent(EventInput, java.util.function.Consumer)}.
- * @hide
*/
-@DataClass(genHiddenBuilder = true, genEqualsHashCode = true)
-public final class EventInput implements Parcelable {
+@FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
+@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, java.util.function.Consumer)}.
@@ -44,6 +46,11 @@ public final class EventInput implements Parcelable {
*/
@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.
@@ -59,8 +66,20 @@ public final class EventInput implements Parcelable {
//@formatter:off
+ /**
+ * Creates a new EventInput.
+ *
+ * @param requestLogRecord
+ * The {@link RequestLogRecord} that was returned as a result of
+ * {@link IsolatedWorker#onExecute(ExecuteInput, java.util.function.Consumer)}.
+ * @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
- /* package-private */ EventInput(
+ public EventInput(
@Nullable RequestLogRecord requestLogRecord,
@NonNull PersistableBundle parameters) {
this.mRequestLogRecord = requestLogRecord;
@@ -119,127 +138,11 @@ public final class EventInput implements Parcelable {
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 (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 */ EventInput(@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<EventInput> CREATOR
- = new Parcelable.Creator<EventInput>() {
- @Override
- public EventInput[] newArray(int size) {
- return new EventInput[size];
- }
-
- @Override
- public EventInput createFromParcel(@NonNull android.os.Parcel in) {
- return new EventInput(in);
- }
- };
-
- /**
- * A builder for {@link EventInput}
- * @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, java.util.function.Consumer)}.
- */
- @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 EventInput build() {
- checkNotUsed();
- mBuilderFieldsSet |= 0x4; // Mark builder used
-
- if ((mBuilderFieldsSet & 0x1) == 0) {
- mRequestLogRecord = null;
- }
- if ((mBuilderFieldsSet & 0x2) == 0) {
- mParameters = PersistableBundle.EMPTY;
- }
- EventInput o = new EventInput(
- 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 = 1697062257720L,
+ 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 [android.os.Parcelable]\n@com.android.ondevicepersonalization.internal.util.DataClass(genHiddenBuilder=true, genEqualsHashCode=true)")
+ 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 []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=false, genHiddenConstructor=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/framework/java/android/adservices/ondevicepersonalization/EventInputParcel.java b/framework/java/android/adservices/ondevicepersonalization/EventInputParcel.java
new file mode 100644
index 00000000..7434064d
--- /dev/null
+++ b/framework/java/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, java.util.function.Consumer)}.
+ */
+ @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, java.util.function.Consumer)}.
+ */
+ @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, java.util.function.Consumer)}.
+ */
+ @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]\n@com.android.ondevicepersonalization.internal.util.DataClass(genAidl=false, genHiddenBuilder=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/framework/java/android/adservices/ondevicepersonalization/EventLogRecord.java b/framework/java/android/adservices/ondevicepersonalization/EventLogRecord.java
index a65b2aa4..bb1c6101 100644
--- a/framework/java/android/adservices/ondevicepersonalization/EventLogRecord.java
+++ b/framework/java/android/adservices/ondevicepersonalization/EventLogRecord.java
@@ -28,6 +28,8 @@ import android.os.Parcelable;
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.
@@ -75,15 +77,20 @@ public final class EventLogRecord implements Parcelable {
* The existing {@link RequestLogRecord} that this payload should be associated with. In an
* implementation of
* {@link IsolatedWorker#onExecute(ExecuteInput, java.util.function.Consumer)}, this should be
- * set to a value returned by {@link LogReader#getRequests(long, long)}. In an implementation
- * of {@link IsolatedWorker#onEvent(EventInput, java.util.function.Consumer)}, this should be
- * set to {@code null} because the payload will be automatically associated with the current
- * {@link RequestLogRecord}.
+ * set to a value returned by {@link LogReader#getRequests(Instant, Instant)}. In an
+ * implementation of {@link IsolatedWorker#onEvent(EventInput, java.util.function.Consumer)},
+ * this should be set to {@code null} because the payload will be automatically associated with
+ * the current {@link RequestLogRecord}.
*
- * @hide
*/
@Nullable RequestLogRecord mRequestLogRecord = null;
+ /**
+ * Returns the timestamp of this record.
+ */
+ @NonNull public Instant getTime() {
+ return Instant.ofEpochMilli(getTimeMillis());
+ }
abstract static class BaseBuilder {
/**
@@ -174,12 +181,10 @@ public final class EventLogRecord implements Parcelable {
* The existing {@link RequestLogRecord} that this payload should be associated with. In an
* implementation of
* {@link IsolatedWorker#onExecute(ExecuteInput, java.util.function.Consumer)}, this should be
- * set to a value returned by {@link LogReader#getRequests(long, long)}. In an implementation
- * of {@link IsolatedWorker#onEvent(EventInput, java.util.function.Consumer)}, this should be
- * set to {@code null} because the payload will be automatically associated with the current
- * {@link RequestLogRecord}.
- *
- * @hide
+ * set to a value returned by {@link LogReader#getRequests(Instant, Instant)}. In an
+ * implementation of {@link IsolatedWorker#onEvent(EventInput, java.util.function.Consumer)},
+ * 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() {
@@ -362,12 +367,10 @@ public final class EventLogRecord implements Parcelable {
* The existing {@link RequestLogRecord} that this payload should be associated with. In an
* implementation of
* {@link IsolatedWorker#onExecute(ExecuteInput, java.util.function.Consumer)}, this should be
- * set to a value returned by {@link LogReader#getRequests(long, long)}. In an implementation
- * of {@link IsolatedWorker#onEvent(EventInput, java.util.function.Consumer)}, this should be
- * set to {@code null} because the payload will be automatically associated with the current
- * {@link RequestLogRecord}.
- *
- * @hide
+ * set to a value returned by {@link LogReader#getRequests(Instant, Instant)}. In an
+ * implementation of {@link IsolatedWorker#onEvent(EventInput, java.util.function.Consumer)},
+ * 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(@NonNull RequestLogRecord value) {
@@ -415,10 +418,10 @@ public final class EventLogRecord implements Parcelable {
}
@DataClass.Generated(
- time = 1697576750150L,
+ time = 1698963996102L,
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 @android.annotation.Nullable android.content.ContentValues mData\n @android.annotation.Nullable android.adservices.ondevicepersonalization.RequestLogRecord mRequestLogRecord\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 []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)\npublic abstract android.adservices.ondevicepersonalization.EventLogRecord.Builder setTimeMillis(long)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "private @android.annotation.IntRange int mRowIndex\nprivate @android.annotation.IntRange int mType\nprivate long mTimeMillis\n @android.annotation.Nullable android.content.ContentValues mData\n @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 []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)\npublic abstract android.adservices.ondevicepersonalization.EventLogRecord.Builder setTimeMillis(long)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/framework/java/android/adservices/ondevicepersonalization/EventOutput.aidl b/framework/java/android/adservices/ondevicepersonalization/EventOutput.aidl
deleted file mode 100644
index e7e0f952..00000000
--- a/framework/java/android/adservices/ondevicepersonalization/EventOutput.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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;
-
-parcelable EventOutput;
diff --git a/framework/java/android/adservices/ondevicepersonalization/EventOutput.java b/framework/java/android/adservices/ondevicepersonalization/EventOutput.java
index 82b65750..9fe978d6 100644
--- a/framework/java/android/adservices/ondevicepersonalization/EventOutput.java
+++ b/framework/java/android/adservices/ondevicepersonalization/EventOutput.java
@@ -16,17 +16,19 @@
package android.adservices.ondevicepersonalization;
+import static android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS;
+
+import android.annotation.FlaggedApi;
import android.annotation.Nullable;
-import android.os.Parcelable;
import com.android.ondevicepersonalization.internal.util.DataClass;
/**
* The result returned by {@link IsolatedWorker#onEvent(EventInput, java.util.function.Consumer)}.
- * @hide
*/
+@FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
@DataClass(genBuilder = true, genEqualsHashCode = true)
-public final class EventOutput implements Parcelable {
+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
@@ -94,54 +96,10 @@ public final class EventOutput implements Parcelable {
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 (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 */ EventOutput(@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<EventOutput> CREATOR
- = new Parcelable.Creator<EventOutput>() {
- @Override
- public EventOutput[] newArray(int size) {
- return new EventOutput[size];
- }
-
- @Override
- public EventOutput createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
- return new EventOutput(in);
- }
- };
-
/**
* A builder for {@link EventOutput}
*/
+ @FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
@SuppressWarnings("WeakerAccess")
@DataClass.Generated.Member
public static final class Builder {
@@ -188,10 +146,10 @@ public final class EventOutput implements Parcelable {
}
@DataClass.Generated(
- time = 1696369232183L,
+ time = 1698882423016L,
codegenVersion = "1.0.23",
sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/EventOutput.java",
- inputSignatures = " @android.annotation.Nullable android.adservices.ondevicepersonalization.EventLogRecord mEventLogRecord\nclass EventOutput extends java.lang.Object implements [android.os.Parcelable]\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)")
+ inputSignatures = " @android.annotation.Nullable android.adservices.ondevicepersonalization.EventLogRecord mEventLogRecord\nclass EventOutput extends java.lang.Object implements []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/framework/java/android/adservices/ondevicepersonalization/EventOutputParcel.java b/framework/java/android/adservices/ondevicepersonalization/EventOutputParcel.java
new file mode 100644
index 00000000..5432a6c4
--- /dev/null
+++ b/framework/java/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]\n@com.android.ondevicepersonalization.internal.util.DataClass(genAidl=false, genBuilder=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/framework/java/android/adservices/ondevicepersonalization/ExecuteInput.aidl b/framework/java/android/adservices/ondevicepersonalization/ExecuteInput.aidl
deleted file mode 100644
index 14f342f9..00000000
--- a/framework/java/android/adservices/ondevicepersonalization/ExecuteInput.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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;
-
-parcelable ExecuteInput;
diff --git a/framework/java/android/adservices/ondevicepersonalization/ExecuteInput.java b/framework/java/android/adservices/ondevicepersonalization/ExecuteInput.java
index b7fbdb8b..825b25a2 100644
--- a/framework/java/android/adservices/ondevicepersonalization/ExecuteInput.java
+++ b/framework/java/android/adservices/ondevicepersonalization/ExecuteInput.java
@@ -20,7 +20,6 @@ import static android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ON
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
-import android.os.Parcelable;
import android.os.PersistableBundle;
import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
@@ -31,8 +30,8 @@ import com.android.ondevicepersonalization.internal.util.DataClass;
*
*/
@FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
-@DataClass(genHiddenBuilder = true, genEqualsHashCode = true)
-public final class ExecuteInput implements Parcelable {
+@DataClass(genBuilder = false, genHiddenConstructor = true, genEqualsHashCode = true)
+public final class ExecuteInput {
/**
* The package name of the calling app.
*/
@@ -44,6 +43,10 @@ public final class ExecuteInput implements Parcelable {
*/
@NonNull PersistableBundle mAppParams = PersistableBundle.EMPTY;
+ /** @hide */
+ public ExecuteInput(@NonNull ExecuteInputParcel parcel) {
+ this(parcel.getAppPackageName(), parcel.getAppParams());
+ }
@@ -60,8 +63,18 @@ public final class ExecuteInput implements Parcelable {
//@formatter:off
+ /**
+ * Creates a new ExecuteInput.
+ *
+ * @param appPackageName
+ * The package name of the calling app.
+ * @param appParams
+ * The parameters provided by the app to the {@link IsolatedService}. The service
+ * defines the expected keys in this {@link PersistableBundle}.
+ * @hide
+ */
@DataClass.Generated.Member
- /* package-private */ ExecuteInput(
+ public ExecuteInput(
@NonNull String appPackageName,
@NonNull PersistableBundle appParams) {
this.mAppPackageName = appPackageName;
@@ -120,123 +133,11 @@ public final class ExecuteInput implements Parcelable {
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.writeString(mAppPackageName);
- dest.writeTypedObject(mAppParams, flags);
- }
-
- @Override
- @DataClass.Generated.Member
- public int describeContents() { return 0; }
-
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- @DataClass.Generated.Member
- /* package-private */ ExecuteInput(@NonNull android.os.Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
- String appPackageName = in.readString();
- PersistableBundle appParams = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR);
-
- this.mAppPackageName = appPackageName;
- AnnotationValidations.validate(
- NonNull.class, null, mAppPackageName);
- this.mAppParams = appParams;
- AnnotationValidations.validate(
- NonNull.class, null, mAppParams);
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- @DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<ExecuteInput> CREATOR
- = new Parcelable.Creator<ExecuteInput>() {
- @Override
- public ExecuteInput[] newArray(int size) {
- return new ExecuteInput[size];
- }
-
- @Override
- public ExecuteInput createFromParcel(@NonNull android.os.Parcel in) {
- return new ExecuteInput(in);
- }
- };
-
- /**
- * A builder for {@link ExecuteInput}
- * @hide
- */
- @SuppressWarnings("WeakerAccess")
- @DataClass.Generated.Member
- public static final class Builder {
-
- private @NonNull String mAppPackageName;
- private @NonNull PersistableBundle mAppParams;
-
- 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;
- }
-
- /**
- * The parameters provided by the app to the {@link IsolatedService}. The service
- * defines the expected keys in this {@link PersistableBundle}.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setAppParams(@NonNull PersistableBundle value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x2;
- mAppParams = value;
- return this;
- }
-
- /** Builds the instance. This builder should not be touched after calling this! */
- public @NonNull ExecuteInput build() {
- checkNotUsed();
- mBuilderFieldsSet |= 0x4; // Mark builder used
-
- if ((mBuilderFieldsSet & 0x1) == 0) {
- mAppPackageName = "";
- }
- if ((mBuilderFieldsSet & 0x2) == 0) {
- mAppParams = PersistableBundle.EMPTY;
- }
- ExecuteInput o = new ExecuteInput(
- mAppPackageName,
- mAppParams);
- 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 = 1697051118497L,
+ time = 1698879004862L,
codegenVersion = "1.0.23",
sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/ExecuteInput.java",
- inputSignatures = " @android.annotation.NonNull java.lang.String mAppPackageName\n @android.annotation.NonNull android.os.PersistableBundle mAppParams\nclass ExecuteInput extends java.lang.Object implements [android.os.Parcelable]\n@com.android.ondevicepersonalization.internal.util.DataClass(genHiddenBuilder=true, genEqualsHashCode=true)")
+ inputSignatures = " @android.annotation.NonNull java.lang.String mAppPackageName\n @android.annotation.NonNull android.os.PersistableBundle mAppParams\nclass ExecuteInput extends java.lang.Object implements []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=false, genHiddenConstructor=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/framework/java/android/adservices/ondevicepersonalization/ExecuteInputParcel.java b/framework/java/android/adservices/ondevicepersonalization/ExecuteInputParcel.java
new file mode 100644
index 00000000..44c60f30
--- /dev/null
+++ b/framework/java/android/adservices/ondevicepersonalization/ExecuteInputParcel.java
@@ -0,0 +1,213 @@
+/*
+ * 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 android.os.PersistableBundle;
+
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+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 = "";
+
+ /**
+ * The parameters provided by the app to the {@link IsolatedService}. The service
+ * defines the expected keys in this {@link PersistableBundle}.
+ */
+ @NonNull PersistableBundle mAppParams = 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/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,
+ @NonNull PersistableBundle appParams) {
+ this.mAppPackageName = appPackageName;
+ AnnotationValidations.validate(
+ NonNull.class, null, mAppPackageName);
+ this.mAppParams = appParams;
+ AnnotationValidations.validate(
+ NonNull.class, null, mAppParams);
+
+ // 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;
+ }
+
+ /**
+ * The parameters provided by the app to the {@link IsolatedService}. The service
+ * defines the expected keys in this {@link PersistableBundle}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull PersistableBundle getAppParams() {
+ return mAppParams;
+ }
+
+ @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.writeString(mAppPackageName);
+ dest.writeTypedObject(mAppParams, 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) { ... }
+
+ String appPackageName = in.readString();
+ PersistableBundle appParams = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR);
+
+ this.mAppPackageName = appPackageName;
+ AnnotationValidations.validate(
+ NonNull.class, null, mAppPackageName);
+ this.mAppParams = appParams;
+ AnnotationValidations.validate(
+ NonNull.class, null, mAppParams);
+
+ // 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 @NonNull PersistableBundle mAppParams;
+
+ 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;
+ }
+
+ /**
+ * The parameters provided by the app to the {@link IsolatedService}. The service
+ * defines the expected keys in this {@link PersistableBundle}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setAppParams(@NonNull PersistableBundle value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mAppParams = 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) {
+ mAppParams = PersistableBundle.EMPTY;
+ }
+ ExecuteInputParcel o = new ExecuteInputParcel(
+ mAppPackageName,
+ mAppParams);
+ 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 = 1698868678877L,
+ 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.NonNull android.os.PersistableBundle mAppParams\nclass ExecuteInputParcel extends java.lang.Object implements [android.os.Parcelable]\n@com.android.ondevicepersonalization.internal.util.DataClass(genAidl=false, genHiddenBuilder=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.aidl b/framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.aidl
deleted file mode 100644
index 9746ecd6..00000000
--- a/framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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;
-
-parcelable ExecuteOutput;
diff --git a/framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.java b/framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.java
index ad7c5d2e..b6ee67dd 100644
--- a/framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.java
+++ b/framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.java
@@ -21,7 +21,6 @@ import static android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ON
import android.annotation.FlaggedApi;
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;
@@ -38,7 +37,7 @@ import java.util.List;
*/
@FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
@DataClass(genBuilder = true, genEqualsHashCode = true)
-public final class ExecuteOutput implements Parcelable {
+public final class ExecuteOutput {
/**
* Persistent data to be written to the REQUESTS table after
* {@link IsolatedWorker#onExecute(ExecuteInput, java.util.function.Consumer)}
@@ -160,63 +159,6 @@ public final class ExecuteOutput implements Parcelable {
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 (mRequestLogRecord != null) flg |= 0x1;
- dest.writeByte(flg);
- if (mRequestLogRecord != null) dest.writeTypedObject(mRequestLogRecord, flags);
- dest.writeParcelableList(mRenderingConfigs, flags);
- dest.writeParcelableList(mEventLogRecords, flags);
- }
-
- @Override
- @DataClass.Generated.Member
- public int describeContents() { return 0; }
-
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- @DataClass.Generated.Member
- /* package-private */ ExecuteOutput(@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<RenderingConfig> renderingConfigs = new java.util.ArrayList<>();
- in.readParcelableList(renderingConfigs, RenderingConfig.class.getClassLoader());
- List<EventLogRecord> eventLogRecords = new java.util.ArrayList<>();
- in.readParcelableList(eventLogRecords, EventLogRecord.class.getClassLoader());
-
- this.mRequestLogRecord = requestLogRecord;
- this.mRenderingConfigs = renderingConfigs;
- AnnotationValidations.validate(
- NonNull.class, null, mRenderingConfigs);
- 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<ExecuteOutput> CREATOR
- = new Parcelable.Creator<ExecuteOutput>() {
- @Override
- public ExecuteOutput[] newArray(int size) {
- return new ExecuteOutput[size];
- }
-
- @Override
- public ExecuteOutput createFromParcel(@NonNull android.os.Parcel in) {
- return new ExecuteOutput(in);
- }
- };
-
/**
* A builder for {@link ExecuteOutput}
*/
@@ -284,10 +226,7 @@ public final class ExecuteOutput implements Parcelable {
return this;
}
- /**
- * @see #setEventLogRecords
- * @hide
- */
+ /** @see #setEventLogRecords @hide */
@DataClass.Generated.Member
public @NonNull Builder addEventLogRecord(@NonNull EventLogRecord value) {
if (mEventLogRecords == null) setEventLogRecords(new java.util.ArrayList<>());
@@ -325,10 +264,10 @@ public final class ExecuteOutput implements Parcelable {
}
@DataClass.Generated(
- time = 1697132452641L,
+ time = 1698880049845L,
codegenVersion = "1.0.23",
sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/ExecuteOutput.java",
- inputSignatures = "private @android.annotation.Nullable android.adservices.ondevicepersonalization.RequestLogRecord mRequestLogRecord\nprivate @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"renderingConfig\") @android.annotation.NonNull java.util.List<android.adservices.ondevicepersonalization.RenderingConfig> mRenderingConfigs\nprivate @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"eventLogRecord\") @android.annotation.NonNull java.util.List<android.adservices.ondevicepersonalization.EventLogRecord> mEventLogRecords\nclass ExecuteOutput extends java.lang.Object implements [android.os.Parcelable]\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)")
+ inputSignatures = "private @android.annotation.Nullable android.adservices.ondevicepersonalization.RequestLogRecord mRequestLogRecord\nprivate @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"renderingConfig\") @android.annotation.NonNull java.util.List<android.adservices.ondevicepersonalization.RenderingConfig> mRenderingConfigs\nprivate @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"eventLogRecord\") @android.annotation.NonNull java.util.List<android.adservices.ondevicepersonalization.EventLogRecord> mEventLogRecords\nclass ExecuteOutput extends java.lang.Object implements []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/framework/java/android/adservices/ondevicepersonalization/ExecuteOutputParcel.java b/framework/java/android/adservices/ondevicepersonalization/ExecuteOutputParcel.java
new file mode 100644
index 00000000..df23d7cf
--- /dev/null
+++ b/framework/java/android/adservices/ondevicepersonalization/ExecuteOutputParcel.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.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, java.util.function.Consumer)}
+ * completes. If null, no persistent data will be written.
+ */
+ @Nullable private RequestLogRecord mRequestLogRecord = null;
+
+ /**
+ * A list of {@link RenderingConfig} objects, one per slot specified in the request from the
+ * calling app. The calling app and the service must agree on the expected size of this list.
+ */
+ @DataClass.PluralOf("renderingConfig")
+ @NonNull private List<RenderingConfig> mRenderingConfigs = Collections.emptyList();
+
+ /**
+ * 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.
+ *
+ * @hide
+ */
+ @DataClass.PluralOf("eventLogRecord")
+ @NonNull private List<EventLogRecord> mEventLogRecords = Collections.emptyList();
+
+ /** @hide */
+ public ExecuteOutputParcel(@NonNull ExecuteOutput value) {
+ this(value.getRequestLogRecord(), value.getRenderingConfigs(), 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/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, java.util.function.Consumer)}
+ * completes. If null, no persistent data will be written.
+ * @param renderingConfigs
+ * A list of {@link RenderingConfig} objects, one per slot specified in the request from the
+ * calling app. The calling app and the service must agree on the expected size of this list.
+ * @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.
+ */
+ @DataClass.Generated.Member
+ public ExecuteOutputParcel(
+ @Nullable RequestLogRecord requestLogRecord,
+ @NonNull List<RenderingConfig> renderingConfigs,
+ @NonNull List<EventLogRecord> eventLogRecords) {
+ this.mRequestLogRecord = requestLogRecord;
+ this.mRenderingConfigs = renderingConfigs;
+ AnnotationValidations.validate(
+ NonNull.class, null, mRenderingConfigs);
+ 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#onExecute(ExecuteInput, java.util.function.Consumer)}
+ * completes. If null, no persistent data will be written.
+ */
+ @DataClass.Generated.Member
+ public @Nullable RequestLogRecord getRequestLogRecord() {
+ return mRequestLogRecord;
+ }
+
+ /**
+ * A list of {@link RenderingConfig} objects, one per slot specified in the request from the
+ * calling app. The calling app and the service must agree on the expected size of this list.
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<RenderingConfig> getRenderingConfigs() {
+ return mRenderingConfigs;
+ }
+
+ /**
+ * 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.
+ *
+ * @hide
+ */
+ @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(mRenderingConfigs, flags);
+ dest.writeParcelableList(mEventLogRecords, flags);
+ }
+
+ @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);
+ List<RenderingConfig> renderingConfigs = new java.util.ArrayList<>();
+ in.readParcelableList(renderingConfigs, RenderingConfig.class.getClassLoader());
+ List<EventLogRecord> eventLogRecords = new java.util.ArrayList<>();
+ in.readParcelableList(eventLogRecords, EventLogRecord.class.getClassLoader());
+
+ this.mRequestLogRecord = requestLogRecord;
+ this.mRenderingConfigs = renderingConfigs;
+ AnnotationValidations.validate(
+ NonNull.class, null, mRenderingConfigs);
+ 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<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 = 1698864579986L,
+ 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 @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"renderingConfig\") @android.annotation.NonNull java.util.List<android.adservices.ondevicepersonalization.RenderingConfig> mRenderingConfigs\nprivate @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"eventLogRecord\") @android.annotation.NonNull java.util.List<android.adservices.ondevicepersonalization.EventLogRecord> mEventLogRecords\nclass ExecuteOutputParcel extends java.lang.Object implements [android.os.Parcelable]\n@com.android.ondevicepersonalization.internal.util.DataClass(genAidl=false, genBuilder=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/framework/java/android/adservices/ondevicepersonalization/FederatedComputeInput.java b/framework/java/android/adservices/ondevicepersonalization/FederatedComputeInput.java
index e5dee4c5..6057b2c1 100644
--- a/framework/java/android/adservices/ondevicepersonalization/FederatedComputeInput.java
+++ b/framework/java/android/adservices/ondevicepersonalization/FederatedComputeInput.java
@@ -16,54 +16,49 @@
package android.adservices.ondevicepersonalization;
+import static android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
import com.android.ondevicepersonalization.internal.util.DataClass;
-/**
- * The input data for
- * {@link FederatedComputeScheduler#schedule(FederatedComputeScheduler.Params, FederatedComputeInput)}
- *
- * @hide
- */
+/** The input data for {@link FederatedComputeScheduler#schedule}. */
@DataClass(genBuilder = true, genEqualsHashCode = true)
+@FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
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 computation server.
+ * 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
+ // $ 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
-
+ // @formatter:off
@DataClass.Generated.Member
- /* package-private */ FederatedComputeInput(
- @NonNull String populationName) {
+ /* package-private */ FederatedComputeInput(@NonNull String populationName) {
this.mPopulationName = populationName;
- AnnotationValidations.validate(
- NonNull.class, null, mPopulationName);
+ 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 computation server.
+ * match task plan configured at remote federated compute server.
*/
@DataClass.Generated.Member
public @NonNull String getPopulationName() {
@@ -82,8 +77,7 @@ public final class FederatedComputeInput {
@SuppressWarnings("unchecked")
FederatedComputeInput that = (FederatedComputeInput) o;
//noinspection PointlessBooleanExpression
- return true
- && java.util.Objects.equals(mPopulationName, that.mPopulationName);
+ return true && java.util.Objects.equals(mPopulationName, that.mPopulationName);
}
@Override
@@ -97,9 +91,8 @@ public final class FederatedComputeInput {
return _hash;
}
- /**
- * A builder for {@link FederatedComputeInput}
- */
+ /** A builder for {@link FederatedComputeInput} */
+ @FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
@SuppressWarnings("WeakerAccess")
@DataClass.Generated.Member
public static final class Builder {
@@ -108,13 +101,9 @@ public final class FederatedComputeInput {
private long mBuilderFieldsSet = 0L;
- public Builder() {
- }
+ public Builder() {}
- /**
- * Population refers to a collection of devices that specific task groups can run on. It should
- * match task plan configured at remote federated computation server.
- */
+ /** Setter for {@link #getPopulationName}. */
@DataClass.Generated.Member
public @NonNull Builder setPopulationName(@NonNull String value) {
checkNotUsed();
@@ -131,8 +120,7 @@ public final class FederatedComputeInput {
if ((mBuilderFieldsSet & 0x1) == 0) {
mPopulationName = "";
}
- FederatedComputeInput o = new FederatedComputeInput(
- mPopulationName);
+ FederatedComputeInput o = new FederatedComputeInput(mPopulationName);
return o;
}
@@ -147,13 +135,14 @@ public final class FederatedComputeInput {
@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 []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)")
+ 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 []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
-
- //@formatter:on
+ // @formatter:on
// End of generated code
}
diff --git a/framework/java/android/adservices/ondevicepersonalization/FederatedComputeScheduler.java b/framework/java/android/adservices/ondevicepersonalization/FederatedComputeScheduler.java
index 4077e7e8..6e6f2bef 100644
--- a/framework/java/android/adservices/ondevicepersonalization/FederatedComputeScheduler.java
+++ b/framework/java/android/adservices/ondevicepersonalization/FederatedComputeScheduler.java
@@ -16,8 +16,11 @@
package android.adservices.ondevicepersonalization;
+import static android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS;
+
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;
@@ -28,10 +31,10 @@ import com.android.ondevicepersonalization.internal.util.LoggerFactory;
import java.util.concurrent.CountDownLatch;
/**
- * Handles scheduling Federated Learning and Federated Analytics jobs.
- *
- * @hide
+ * Handles scheduling federated compute jobs. See {@link
+ * IsolatedService#getFederatedComputeScheduler}.
*/
+@FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
public class FederatedComputeScheduler {
private static final String TAG = FederatedComputeScheduler.class.getSimpleName();
private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
@@ -44,14 +47,15 @@ public class FederatedComputeScheduler {
}
// TODO(b/300461799): add federated compute server document.
+ // TODO(b/269665435): add sample code snippet.
/**
- * Schedule a federated computation job.
+ * 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 computation. It should be consistent with
- * the federated computation server setup.
- * @throws IllegalArgumentException caused by caller supplied invalid input argument.
- * @throws IllegalStateException caused by an internal failure of FederatedComputeScheduler.
+ * @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) {
@@ -94,7 +98,10 @@ public class FederatedComputeScheduler {
}
/**
- * Cancel a federated computation job with input training params.
+ * 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 populationName population name of the job that caller wants to cancel
* @throws IllegalStateException caused by an internal failure of FederatedComputeScheduler.
@@ -141,6 +148,7 @@ public class FederatedComputeScheduler {
}
/** The parameters related to job scheduling. */
+ @FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
public static class Params {
/**
* If training interval is scheduled for recurrent tasks, the earliest time this task could
diff --git a/framework/java/android/adservices/ondevicepersonalization/IsolatedService.java b/framework/java/android/adservices/ondevicepersonalization/IsolatedService.java
index f9fcc59b..5a8fd11a 100644
--- a/framework/java/android/adservices/ondevicepersonalization/IsolatedService.java
+++ b/framework/java/android/adservices/ondevicepersonalization/IsolatedService.java
@@ -40,6 +40,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
+import java.util.function.Function;
// TODO(b/289102463): Add a link to the public ODP developer documentation.
/**
@@ -144,8 +145,6 @@ public abstract class IsolatedService extends Service {
* 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)
- *
- * @hide
*/
@NonNull
public final LogReader getLogReader(@NonNull RequestToken requestToken) {
@@ -181,14 +180,13 @@ public abstract class IsolatedService extends Service {
/**
* 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.
+ * 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)
- * @hide
*/
@NonNull
public final FederatedComputeScheduler getFederatedComputeScheduler(
@@ -210,9 +208,9 @@ public abstract class IsolatedService extends Service {
if (operationCode == Constants.OP_EXECUTE) {
- ExecuteInput input =
- Objects.requireNonNull(
- params.getParcelable(Constants.EXTRA_INPUT, ExecuteInput.class));
+ ExecuteInputParcel inputParcel = Objects.requireNonNull(
+ params.getParcelable(Constants.EXTRA_INPUT, ExecuteInputParcel.class));
+ ExecuteInput input = new ExecuteInput(inputParcel);
Objects.requireNonNull(input.getAppPackageName());
IDataAccessService binder =
IDataAccessService.Stub.asInterface(
@@ -229,8 +227,10 @@ public abstract class IsolatedService extends Service {
UserData userData = params.getParcelable(Constants.EXTRA_USER_DATA, UserData.class);
RequestToken requestToken = new RequestToken(binder, fcBinder, userData);
IsolatedWorker implCallback = IsolatedService.this.onRequest(requestToken);
- implCallback.onExecute(input, new WrappedCallback<ExecuteOutput>(
- resultCallback, requestToken));
+ implCallback.onExecute(
+ input,
+ new WrappedCallback<ExecuteOutput, ExecuteOutputParcel>(
+ resultCallback, requestToken, v -> new ExecuteOutputParcel(v)));
} else if (operationCode == Constants.OP_DOWNLOAD) {
@@ -274,14 +274,19 @@ public abstract class IsolatedService extends Service {
RequestToken requestToken = new RequestToken(binder, fcBinder, userData);
IsolatedWorker implCallback = IsolatedService.this.onRequest(requestToken);
implCallback.onDownloadCompleted(
- input, new WrappedCallback<DownloadCompletedOutput>(
- resultCallback, requestToken));
+ input,
+ new WrappedCallback<DownloadCompletedOutput,
+ DownloadCompletedOutputParcel>(
+ resultCallback,
+ requestToken,
+ v -> new DownloadCompletedOutputParcel(v)));
} else if (operationCode == Constants.OP_RENDER) {
- RenderInput input =
- Objects.requireNonNull(
- params.getParcelable(Constants.EXTRA_INPUT, RenderInput.class));
+ RenderInputParcel inputParcel =
+ Objects.requireNonNull(params.getParcelable(
+ Constants.EXTRA_INPUT, RenderInputParcel.class));
+ RenderInput input = new RenderInput(inputParcel);
Objects.requireNonNull(input.getRenderingConfig());
IDataAccessService binder =
IDataAccessService.Stub.asInterface(
@@ -291,15 +296,16 @@ public abstract class IsolatedService extends Service {
Objects.requireNonNull(binder);
RequestToken requestToken = new RequestToken(binder, null, null);
IsolatedWorker implCallback = IsolatedService.this.onRequest(requestToken);
- implCallback.onRender(input, new WrappedCallback<RenderOutput>(
- resultCallback, requestToken));
+ implCallback.onRender(input, new WrappedCallback<RenderOutput, RenderOutputParcel>(
+ resultCallback, requestToken, v -> new RenderOutputParcel(v)));
} else if (operationCode == Constants.OP_WEB_VIEW_EVENT) {
- EventInput input =
+ EventInputParcel inputParcel =
Objects.requireNonNull(
params.getParcelable(
- Constants.EXTRA_INPUT, EventInput.class));
+ Constants.EXTRA_INPUT, EventInputParcel.class));
+ EventInput input = new EventInput(inputParcel);
IDataAccessService binder =
IDataAccessService.Stub.asInterface(
Objects.requireNonNull(
@@ -309,13 +315,15 @@ public abstract class IsolatedService extends Service {
RequestToken requestToken = new RequestToken(binder, null, userData);
IsolatedWorker implCallback = IsolatedService.this.onRequest(requestToken);
implCallback.onEvent(
- input, new WrappedCallback<EventOutput>(resultCallback, requestToken));
+ input, new WrappedCallback<EventOutput, EventOutputParcel>(
+ resultCallback, requestToken, v -> new EventOutputParcel(v)));
} else if (operationCode == Constants.OP_TRAINING_EXAMPLE) {
- TrainingExampleInput input =
+ TrainingExamplesInputParcel inputParcel =
Objects.requireNonNull(
params.getParcelable(
- Constants.EXTRA_INPUT, TrainingExampleInput.class));
+ Constants.EXTRA_INPUT, TrainingExamplesInputParcel.class));
+ TrainingExamplesInput input = new TrainingExamplesInput(inputParcel);
IDataAccessService binder =
IDataAccessService.Stub.asInterface(
Objects.requireNonNull(
@@ -325,12 +333,14 @@ public abstract class IsolatedService extends Service {
UserData userData = params.getParcelable(Constants.EXTRA_USER_DATA, UserData.class);
RequestToken requestToken = new RequestToken(binder, null, userData);
IsolatedWorker implCallback = IsolatedService.this.onRequest(requestToken);
- implCallback.onTrainingExample(
- input, new Consumer<TrainingExampleOutput>() {
+ implCallback.onTrainingExamples(
+ input,
+ new Consumer<TrainingExamplesOutput>() {
@Override
- public void accept(TrainingExampleOutput result) {
- long elapsedTimeMillis = SystemClock.elapsedRealtime()
- - requestToken.getStartTimeMillis();
+ public void accept(TrainingExamplesOutput result) {
+ long elapsedTimeMillis =
+ SystemClock.elapsedRealtime()
+ - requestToken.getStartTimeMillis();
if (result == null) {
try {
resultCallback.onError(Constants.STATUS_INTERNAL_ERROR);
@@ -338,8 +348,8 @@ public abstract class IsolatedService extends Service {
sLogger.w(TAG + ": Callback failed.", e);
}
} else {
- TrainingExampleOutputParcel parcelResult =
- new TrainingExampleOutputParcel.Builder()
+ TrainingExamplesOutputParcel parcelResult =
+ new TrainingExamplesOutputParcel.Builder()
.setTrainingExamples(
new ByteArrayParceledListSlice(
result.getTrainingExamples()))
@@ -349,10 +359,11 @@ public abstract class IsolatedService extends Service {
.build();
Bundle bundle = new Bundle();
bundle.putParcelable(Constants.EXTRA_RESULT, parcelResult);
- bundle.putParcelable(Constants.EXTRA_CALLEE_METADATA,
+ bundle.putParcelable(
+ Constants.EXTRA_CALLEE_METADATA,
new CalleeMetadata.Builder()
- .setElapsedTimeMillis(elapsedTimeMillis)
- .build());
+ .setElapsedTimeMillis(elapsedTimeMillis)
+ .build());
try {
resultCallback.onSuccess(bundle);
} catch (RemoteException e) {
@@ -367,13 +378,18 @@ public abstract class IsolatedService extends Service {
}
}
- private static class WrappedCallback<T extends Parcelable> implements Consumer<T> {
+ private static class WrappedCallback<T, U extends Parcelable> implements Consumer<T> {
@NonNull private final IIsolatedServiceCallback mCallback;
@NonNull private final RequestToken mRequestToken;
+ @NonNull private final Function<T, U> mConverter;
- WrappedCallback(IIsolatedServiceCallback callback, RequestToken requestToken) {
+ WrappedCallback(
+ IIsolatedServiceCallback callback,
+ RequestToken requestToken,
+ Function<T, U> converter) {
mCallback = Objects.requireNonNull(callback);
mRequestToken = Objects.requireNonNull(requestToken);
+ mConverter = Objects.requireNonNull(converter);
}
@Override
@@ -388,7 +404,8 @@ public abstract class IsolatedService extends Service {
}
} else {
Bundle bundle = new Bundle();
- bundle.putParcelable(Constants.EXTRA_RESULT, result);
+ U wrappedResult = mConverter.apply(result);
+ bundle.putParcelable(Constants.EXTRA_RESULT, wrappedResult);
bundle.putParcelable(Constants.EXTRA_CALLEE_METADATA,
new CalleeMetadata.Builder()
.setElapsedTimeMillis(elapsedTimeMillis)
diff --git a/framework/java/android/adservices/ondevicepersonalization/IsolatedWorker.java b/framework/java/android/adservices/ondevicepersonalization/IsolatedWorker.java
index 39adfc36..11a651eb 100644
--- a/framework/java/android/adservices/ondevicepersonalization/IsolatedWorker.java
+++ b/framework/java/android/adservices/ondevicepersonalization/IsolatedWorker.java
@@ -106,7 +106,6 @@ public interface IsolatedWorker {
* an error. If called with <code>null</code>, no data is written to the EVENTS table.
* <p>If this method throws a {@link RuntimeException}, no data is written to the EVENTS
* table.
- * @hide
*/
default void onEvent(
@NonNull EventInput input, @NonNull Consumer<EventOutput> consumer) {
@@ -114,18 +113,18 @@ public interface IsolatedWorker {
}
/**
- * Generate a single training example used for federated computation job.
+ * 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 consumer Callback that receives the result. Should be called with <code>null</code> on
* an error. If called with <code>null</code>, no training examples is produced for this
- * training session. <p>If this method throws a {@link RuntimeException}, no training
- * examples are produced for this training session.
- * @hide
+ * training session.
*/
- default void onTrainingExample(
- @NonNull TrainingExampleInput input,
- @NonNull Consumer<TrainingExampleOutput> consumer) {
+ default void onTrainingExamples(
+ @NonNull TrainingExamplesInput input,
+ @NonNull Consumer<TrainingExamplesOutput> consumer) {
consumer.accept(null);
}
}
diff --git a/framework/java/android/adservices/ondevicepersonalization/LogReader.java b/framework/java/android/adservices/ondevicepersonalization/LogReader.java
index 7d54dd55..bf25efcf 100644
--- a/framework/java/android/adservices/ondevicepersonalization/LogReader.java
+++ b/framework/java/android/adservices/ondevicepersonalization/LogReader.java
@@ -16,8 +16,11 @@
package android.adservices.ondevicepersonalization;
+import static android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS;
+
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;
@@ -27,6 +30,7 @@ import android.os.RemoteException;
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;
@@ -39,8 +43,8 @@ import java.util.concurrent.BlockingQueue;
*
* @see IsolatedService#getLogReader(RequestToken)
*
- * @hide
*/
+@FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
public class LogReader {
private static final String TAG = "LogReader";
private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
@@ -60,7 +64,10 @@ public class LogReader {
*/
@WorkerThread
@NonNull
- public List<RequestLogRecord> getRequests(long startTimeMillis, long endTimeMillis) {
+ public List<RequestLogRecord> getRequests(
+ @NonNull Instant startTime, @NonNull Instant endTime) {
+ long startTimeMillis = startTime.toEpochMilli();
+ long endTimeMillis = endTime.toEpochMilli();
if (endTimeMillis <= startTimeMillis) {
throw new IllegalArgumentException(
"endTimeMillis must be greater than startTimeMillis");
@@ -82,7 +89,10 @@ public class LogReader {
*/
@WorkerThread
@NonNull
- public List<EventLogRecord> getJoinedEvents(long startTimeMillis, long endTimeMillis) {
+ public List<EventLogRecord> getJoinedEvents(
+ @NonNull Instant startTime, @NonNull Instant endTime) {
+ long startTimeMillis = startTime.toEpochMilli();
+ long endTimeMillis = endTime.toEpochMilli();
if (endTimeMillis <= startTimeMillis) {
throw new IllegalArgumentException(
"endTimeMillis must be greater than startTimeMillis");
diff --git a/framework/java/android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java b/framework/java/android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java
index 5c0e9b5c..ab772f3a 100644
--- a/framework/java/android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java
+++ b/framework/java/android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java
@@ -111,7 +111,6 @@ public class OnDevicePersonalizationManager {
* 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.
- * @hide
*/
public void execute(
@NonNull ComponentName handler,
diff --git a/framework/java/android/adservices/ondevicepersonalization/RenderInput.aidl b/framework/java/android/adservices/ondevicepersonalization/RenderInput.aidl
deleted file mode 100644
index dc5d618a..00000000
--- a/framework/java/android/adservices/ondevicepersonalization/RenderInput.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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;
-
-parcelable RenderInput;
diff --git a/framework/java/android/adservices/ondevicepersonalization/RenderInput.java b/framework/java/android/adservices/ondevicepersonalization/RenderInput.java
index a9a41df7..0c5824de 100644
--- a/framework/java/android/adservices/ondevicepersonalization/RenderInput.java
+++ b/framework/java/android/adservices/ondevicepersonalization/RenderInput.java
@@ -19,8 +19,8 @@ package android.adservices.ondevicepersonalization;
import static android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS;
import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.Parcelable;
import com.android.ondevicepersonalization.internal.util.DataClass;
@@ -30,8 +30,8 @@ import com.android.ondevicepersonalization.internal.util.DataClass;
*
*/
@FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
-@DataClass(genHiddenBuilder = true, genEqualsHashCode = true)
-public final class RenderInput implements Parcelable {
+@DataClass(genBuilder = false, genHiddenConstructor = true, genEqualsHashCode = true)
+public final class RenderInput {
/** The width of the slot. */
private int mWidth = 0;
@@ -50,6 +50,12 @@ public final class RenderInput implements Parcelable {
*/
@Nullable RenderingConfig mRenderingConfig = null;
+ /** @hide */
+ public RenderInput(@NonNull RenderInputParcel parcel) {
+ this(parcel.getWidth(), parcel.getHeight(), parcel.getRenderingConfigIndex(),
+ parcel.getRenderingConfig());
+ }
+
// Code below generated by codegen v1.0.23.
@@ -65,8 +71,23 @@ public final class RenderInput implements Parcelable {
//@formatter:off
+ /**
+ * Creates a new RenderInput.
+ *
+ * @param width
+ * The width of the slot.
+ * @param height
+ * The height of the slot.
+ * @param renderingConfigIndex
+ * The index of the {@link RenderingConfig} in {@link ExecuteOutput} that this render
+ * request is for.
+ * @param renderingConfig
+ * A {@link RenderingConfig} within an {@link ExecuteOutput} that was returned by
+ * {@link IsolatedWorker#onExecute(ExecuteInput, java.util.function.Consumer)}.
+ * @hide
+ */
@DataClass.Generated.Member
- /* package-private */ RenderInput(
+ public RenderInput(
int width,
int height,
int renderingConfigIndex,
@@ -146,162 +167,11 @@ public final class RenderInput implements Parcelable {
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 (mRenderingConfig != null) flg |= 0x8;
- dest.writeByte(flg);
- dest.writeInt(mWidth);
- dest.writeInt(mHeight);
- dest.writeInt(mRenderingConfigIndex);
- 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 */ RenderInput(@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();
- int renderingConfigIndex = in.readInt();
- RenderingConfig renderingConfig = (flg & 0x8) == 0 ? null : (RenderingConfig) in.readTypedObject(RenderingConfig.CREATOR);
-
- this.mWidth = width;
- this.mHeight = height;
- this.mRenderingConfigIndex = renderingConfigIndex;
- this.mRenderingConfig = renderingConfig;
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- @DataClass.Generated.Member
- public static final @android.annotation.NonNull Parcelable.Creator<RenderInput> CREATOR
- = new Parcelable.Creator<RenderInput>() {
- @Override
- public RenderInput[] newArray(int size) {
- return new RenderInput[size];
- }
-
- @Override
- public RenderInput createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
- return new RenderInput(in);
- }
- };
-
- /**
- * A builder for {@link RenderInput}
- * @hide
- */
- @SuppressWarnings("WeakerAccess")
- @DataClass.Generated.Member
- public static final class Builder {
-
- private int mWidth;
- private int mHeight;
- private int mRenderingConfigIndex;
- 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;
- }
-
- /**
- * The index of the {@link RenderingConfig} in {@link ExecuteOutput} that this render
- * request is for.
- */
- @DataClass.Generated.Member
- public @android.annotation.NonNull Builder setRenderingConfigIndex(int value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x4;
- mRenderingConfigIndex = value;
- return this;
- }
-
- /**
- * A {@link RenderingConfig} within an {@link ExecuteOutput} that was returned by
- * {@link IsolatedWorker#onExecute(ExecuteInput, java.util.function.Consumer)}.
- */
- @DataClass.Generated.Member
- public @android.annotation.NonNull Builder setRenderingConfig(@android.annotation.NonNull RenderingConfig value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x8;
- mRenderingConfig = value;
- return this;
- }
-
- /** Builds the instance. This builder should not be touched after calling this! */
- public @android.annotation.NonNull RenderInput build() {
- checkNotUsed();
- mBuilderFieldsSet |= 0x10; // Mark builder used
-
- if ((mBuilderFieldsSet & 0x1) == 0) {
- mWidth = 0;
- }
- if ((mBuilderFieldsSet & 0x2) == 0) {
- mHeight = 0;
- }
- if ((mBuilderFieldsSet & 0x4) == 0) {
- mRenderingConfigIndex = 0;
- }
- if ((mBuilderFieldsSet & 0x8) == 0) {
- mRenderingConfig = null;
- }
- RenderInput o = new RenderInput(
- mWidth,
- mHeight,
- mRenderingConfigIndex,
- mRenderingConfig);
- 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 = 1697063555914L,
+ time = 1698879032836L,
codegenVersion = "1.0.23",
sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/RenderInput.java",
- inputSignatures = "private int mWidth\nprivate int mHeight\nprivate int mRenderingConfigIndex\n @android.annotation.Nullable android.adservices.ondevicepersonalization.RenderingConfig mRenderingConfig\nclass RenderInput extends java.lang.Object implements [android.os.Parcelable]\n@com.android.ondevicepersonalization.internal.util.DataClass(genHiddenBuilder=true, genEqualsHashCode=true)")
+ inputSignatures = "private int mWidth\nprivate int mHeight\nprivate int mRenderingConfigIndex\n @android.annotation.Nullable android.adservices.ondevicepersonalization.RenderingConfig mRenderingConfig\nclass RenderInput extends java.lang.Object implements []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=false, genHiddenConstructor=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/framework/java/android/adservices/ondevicepersonalization/RenderInputParcel.java b/framework/java/android/adservices/ondevicepersonalization/RenderInputParcel.java
new file mode 100644
index 00000000..bf2af756
--- /dev/null
+++ b/framework/java/android/adservices/ondevicepersonalization/RenderInputParcel.java
@@ -0,0 +1,274 @@
+/*
+ * 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;
+
+ /**
+ * The index of the {@link RenderingConfig} in {@link ExecuteOutput} that this render
+ * request is for.
+ */
+ private int mRenderingConfigIndex = 0;
+
+ /**
+ * A {@link RenderingConfig} within an {@link ExecuteOutput} that was returned by
+ * {@link IsolatedWorker#onExecute(ExecuteInput, java.util.function.Consumer)}.
+ */
+ @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,
+ int renderingConfigIndex,
+ @Nullable RenderingConfig renderingConfig) {
+ this.mWidth = width;
+ this.mHeight = height;
+ this.mRenderingConfigIndex = renderingConfigIndex;
+ 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;
+ }
+
+ /**
+ * The index of the {@link RenderingConfig} in {@link ExecuteOutput} that this render
+ * request is for.
+ */
+ @DataClass.Generated.Member
+ public int getRenderingConfigIndex() {
+ return mRenderingConfigIndex;
+ }
+
+ /**
+ * A {@link RenderingConfig} within an {@link ExecuteOutput} that was returned by
+ * {@link IsolatedWorker#onExecute(ExecuteInput, java.util.function.Consumer)}.
+ */
+ @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 |= 0x8;
+ dest.writeByte(flg);
+ dest.writeInt(mWidth);
+ dest.writeInt(mHeight);
+ dest.writeInt(mRenderingConfigIndex);
+ 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();
+ int renderingConfigIndex = in.readInt();
+ RenderingConfig renderingConfig = (flg & 0x8) == 0 ? null : (RenderingConfig) in.readTypedObject(RenderingConfig.CREATOR);
+
+ this.mWidth = width;
+ this.mHeight = height;
+ this.mRenderingConfigIndex = renderingConfigIndex;
+ 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 int mRenderingConfigIndex;
+ 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;
+ }
+
+ /**
+ * The index of the {@link RenderingConfig} in {@link ExecuteOutput} that this render
+ * request is for.
+ */
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull Builder setRenderingConfigIndex(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mRenderingConfigIndex = value;
+ return this;
+ }
+
+ /**
+ * A {@link RenderingConfig} within an {@link ExecuteOutput} that was returned by
+ * {@link IsolatedWorker#onExecute(ExecuteInput, java.util.function.Consumer)}.
+ */
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull Builder setRenderingConfig(@android.annotation.NonNull RenderingConfig value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mRenderingConfig = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @android.annotation.NonNull RenderInputParcel build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mWidth = 0;
+ }
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mHeight = 0;
+ }
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mRenderingConfigIndex = 0;
+ }
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mRenderingConfig = null;
+ }
+ RenderInputParcel o = new RenderInputParcel(
+ mWidth,
+ mHeight,
+ mRenderingConfigIndex,
+ mRenderingConfig);
+ 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 = 1698872925083L,
+ codegenVersion = "1.0.23",
+ sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/RenderInputParcel.java",
+ inputSignatures = "private int mWidth\nprivate int mHeight\nprivate int mRenderingConfigIndex\n @android.annotation.Nullable android.adservices.ondevicepersonalization.RenderingConfig mRenderingConfig\nclass RenderInputParcel extends java.lang.Object implements [android.os.Parcelable]\n@com.android.ondevicepersonalization.internal.util.DataClass(genAidl=false, genHiddenBuilder=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/framework/java/android/adservices/ondevicepersonalization/RenderOutput.aidl b/framework/java/android/adservices/ondevicepersonalization/RenderOutput.aidl
deleted file mode 100644
index 42c26fff..00000000
--- a/framework/java/android/adservices/ondevicepersonalization/RenderOutput.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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;
-
-parcelable RenderOutput;
diff --git a/framework/java/android/adservices/ondevicepersonalization/RenderOutput.java b/framework/java/android/adservices/ondevicepersonalization/RenderOutput.java
index 3b7c8dc1..33c8c8b3 100644
--- a/framework/java/android/adservices/ondevicepersonalization/RenderOutput.java
+++ b/framework/java/android/adservices/ondevicepersonalization/RenderOutput.java
@@ -21,7 +21,6 @@ import static android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ON
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.Parcelable;
import android.os.PersistableBundle;
import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
@@ -34,7 +33,7 @@ import com.android.ondevicepersonalization.internal.util.DataClass;
*/
@FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
@DataClass(genBuilder = true, genEqualsHashCode = true)
-public final class RenderOutput implements Parcelable {
+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()}
@@ -146,60 +145,6 @@ public final class RenderOutput implements Parcelable {
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 (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 */ RenderOutput(@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<RenderOutput> CREATOR
- = new Parcelable.Creator<RenderOutput>() {
- @Override
- public RenderOutput[] newArray(int size) {
- return new RenderOutput[size];
- }
-
- @Override
- public RenderOutput createFromParcel(@NonNull android.os.Parcel in) {
- return new RenderOutput(in);
- }
- };
-
/**
* A builder for {@link RenderOutput}
*/
@@ -285,10 +230,10 @@ public final class RenderOutput implements Parcelable {
}
@DataClass.Generated(
- time = 1697132582732L,
+ time = 1698880093023L,
codegenVersion = "1.0.23",
sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/RenderOutput.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 RenderOutput extends java.lang.Object implements [android.os.Parcelable]\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)")
+ 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 RenderOutput extends java.lang.Object implements []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/framework/java/android/adservices/ondevicepersonalization/RenderOutputParcel.java b/framework/java/android/adservices/ondevicepersonalization/RenderOutputParcel.java
new file mode 100644
index 00000000..afd4cfaf
--- /dev/null
+++ b/framework/java/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]\n@com.android.ondevicepersonalization.internal.util.DataClass(genAidl=false, genBuilder=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/framework/java/android/adservices/ondevicepersonalization/RequestLogRecord.java b/framework/java/android/adservices/ondevicepersonalization/RequestLogRecord.java
index 8cd16764..618462e8 100644
--- a/framework/java/android/adservices/ondevicepersonalization/RequestLogRecord.java
+++ b/framework/java/android/adservices/ondevicepersonalization/RequestLogRecord.java
@@ -26,6 +26,7 @@ import android.os.Parcelable;
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;
@@ -60,6 +61,13 @@ public final class RequestLogRecord implements Parcelable {
*/
private long mTimeMillis = 0;
+ /**
+ * Returns the timestamp of this record.
+ */
+ @NonNull public Instant getTime() {
+ return Instant.ofEpochMilli(getTimeMillis());
+ }
+
abstract static class BaseBuilder {
/**
* @hide
@@ -298,10 +306,10 @@ public final class RequestLogRecord implements Parcelable {
}
@DataClass.Generated(
- time = 1696978492795L,
+ 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\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 []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)\npublic abstract android.adservices.ondevicepersonalization.RequestLogRecord.Builder setTimeMillis(long)\nclass BaseBuilder extends java.lang.Object implements []")
+ 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 []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)\npublic abstract android.adservices.ondevicepersonalization.RequestLogRecord.Builder setTimeMillis(long)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/framework/java/android/adservices/ondevicepersonalization/TrainingExampleOutputParcel.aidl b/framework/java/android/adservices/ondevicepersonalization/TrainingExampleOutputParcel.aidl
deleted file mode 100644
index 699e9c59..00000000
--- a/framework/java/android/adservices/ondevicepersonalization/TrainingExampleOutputParcel.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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;
-
-parcelable TrainingExampleOutputParcel;
diff --git a/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesInput.java b/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesInput.java
new file mode 100644
index 00000000..bd8ca99e
--- /dev/null
+++ b/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesInput.java
@@ -0,0 +1,168 @@
+/*
+ * 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.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
+import com.android.ondevicepersonalization.internal.util.DataClass;
+
+/** The input data for {@link IsolatedWorker#onTrainingExamples}. */
+@FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
+@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 TrainingExamplesOutput.Builder#setResumptionTokens}, OnDevicePersonalization will
+ * store it and pass it here for generating new training examples.
+ */
+ @Nullable private byte[] mResumptionToken = null;
+
+ /** @hide */
+ public TrainingExamplesInput(@NonNull TrainingExamplesInputParcel parcel) {
+ this(parcel.getPopulationName(), parcel.getTaskName(), parcel.getResumptionToken());
+ }
+
+ // 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/TrainingExampleInput.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ // @formatter:off
+
+ /**
+ * Creates a new TrainingExampleInput.
+ *
+ * @param populationName The name of the federated compute population.
+ * @param taskName 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.
+ * @param resumptionToken Token used to support the resumption of training.
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public TrainingExamplesInput(
+ @NonNull String populationName,
+ @NonNull String taskName,
+ @Nullable byte[] resumptionToken) {
+ this.mPopulationName = populationName;
+ AnnotationValidations.validate(NonNull.class, null, mPopulationName);
+ this.mTaskName = taskName;
+ AnnotationValidations.validate(NonNull.class, null, mTaskName);
+ this.mResumptionToken = resumptionToken;
+
+ // 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 TrainingExamplesOutput.Builder#setResumptionTokens}, OnDevicePersonalization will
+ * store it and pass it here for generating new training examples.
+ */
+ @DataClass.Generated.Member
+ public @Nullable byte[] getResumptionToken() {
+ return mResumptionToken;
+ }
+
+ @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(TrainingExampleInput 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);
+ }
+
+ @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);
+ return _hash;
+ }
+
+ @DataClass.Generated(
+ time = 1699394018457L,
+ codegenVersion = "1.0.23",
+ sourceFile =
+ "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExampleInput.java",
+ inputSignatures =
+ "private @android.annotation.NonNull java.lang.String mPopulationName\nprivate @android.annotation.NonNull java.lang.String mTaskName\nprivate @android.annotation.Nullable byte[] mResumptionToken\nclass TrainingExampleInput extends java.lang.Object implements []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=false, genHiddenConstructor=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+ // @formatter:on
+ // End of generated code
+
+}
diff --git a/framework/java/android/adservices/ondevicepersonalization/TrainingExampleInput.java b/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesInputParcel.java
index 412f99aa..9e14b545 100644
--- a/framework/java/android/adservices/ondevicepersonalization/TrainingExampleInput.java
+++ b/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesInputParcel.java
@@ -23,53 +23,47 @@ import android.os.Parcelable;
import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
import com.android.ondevicepersonalization.internal.util.DataClass;
-import java.util.function.Consumer;
-
/**
- * The input data for {@link IsolatedWorker#onTrainingExample(TrainingExampleInput, Consumer)}
+ * Parcelable version of {@link TrainingExamplesInput}
*
* @hide
*/
-@DataClass(genHiddenBuilder = true, genEqualsHashCode = true)
-public final class TrainingExampleInput implements Parcelable {
+@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.
+ * 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;
-
-
// 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/TrainingExampleInput.java
+ // $ codegen
+ // $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExampleInputParcel.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
+ // @formatter:off
@DataClass.Generated.Member
- /* package-private */ TrainingExampleInput(
+ /* package-private */ TrainingExamplesInputParcel(
@NonNull String populationName,
@NonNull String taskName,
@Nullable byte[] resumptionToken) {
this.mPopulationName = populationName;
- AnnotationValidations.validate(
- NonNull.class, null, mPopulationName);
+ AnnotationValidations.validate(NonNull.class, null, mPopulationName);
this.mTaskName = taskName;
- AnnotationValidations.validate(
- NonNull.class, null, mTaskName);
+ AnnotationValidations.validate(NonNull.class, null, mTaskName);
this.mResumptionToken = resumptionToken;
// onConstructed(); // You can define this method to get a callback
@@ -102,37 +96,6 @@ public final class TrainingExampleInput implements Parcelable {
@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(TrainingExampleInput other) { ... }
- // boolean fieldNameEquals(FieldType otherValue) { ... }
-
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- @SuppressWarnings("unchecked")
- TrainingExampleInput that = (TrainingExampleInput) 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);
- }
-
- @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);
- 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) { ... }
@@ -149,7 +112,7 @@ public final class TrainingExampleInput implements Parcelable {
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- /* package-private */ TrainingExampleInput(@NonNull android.os.Parcel in) {
+ /* package-private */ TrainingExamplesInputParcel(@NonNull android.os.Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
@@ -158,8 +121,7 @@ public final class TrainingExampleInput implements Parcelable {
byte[] resumptionToken = in.createByteArray();
this.mPopulationName = populationName;
- AnnotationValidations.validate(
- NonNull.class, null, mPopulationName);
+ AnnotationValidations.validate(NonNull.class, null, mPopulationName);
this.mTaskName = taskName;
AnnotationValidations.validate(
NonNull.class, null, mTaskName);
@@ -169,21 +131,22 @@ public final class TrainingExampleInput implements Parcelable {
}
@DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<TrainingExampleInput> CREATOR
- = new Parcelable.Creator<TrainingExampleInput>() {
- @Override
- public TrainingExampleInput[] newArray(int size) {
- return new TrainingExampleInput[size];
- }
-
- @Override
- public TrainingExampleInput createFromParcel(@NonNull android.os.Parcel in) {
- return new TrainingExampleInput(in);
- }
- };
+ 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 TrainingExampleInput}
+ * A builder for {@link TrainingExamplesInputParcel}
+ *
* @hide
*/
@SuppressWarnings("WeakerAccess")
@@ -234,7 +197,7 @@ public final class TrainingExampleInput implements Parcelable {
}
/** Builds the instance. This builder should not be touched after calling this! */
- public @NonNull TrainingExampleInput build() {
+ public @NonNull TrainingExamplesInputParcel build() {
checkNotUsed();
mBuilderFieldsSet |= 0x8; // Mark builder used
@@ -247,10 +210,8 @@ public final class TrainingExampleInput implements Parcelable {
if ((mBuilderFieldsSet & 0x4) == 0) {
mResumptionToken = null;
}
- TrainingExampleInput o = new TrainingExampleInput(
- mPopulationName,
- mTaskName,
- mResumptionToken);
+ TrainingExamplesInputParcel o =
+ new TrainingExamplesInputParcel(mPopulationName, mTaskName, mResumptionToken);
return o;
}
@@ -263,10 +224,10 @@ public final class TrainingExampleInput implements Parcelable {
}
@DataClass.Generated(
- time = 1697577073626L,
+ time = 1699393441579L,
codegenVersion = "1.0.23",
- sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExampleInput.java",
- inputSignatures = "private @android.annotation.NonNull java.lang.String mPopulationName\nprivate @android.annotation.NonNull java.lang.String mTaskName\nprivate @android.annotation.Nullable byte[] mResumptionToken\nclass TrainingExampleInput extends java.lang.Object implements [android.os.Parcelable]\n@com.android.ondevicepersonalization.internal.util.DataClass(genHiddenBuilder=true, genEqualsHashCode=true)")
+ sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExampleInputParcel.java",
+ inputSignatures = "private @android.annotation.NonNull java.lang.String mPopulationName\nprivate @android.annotation.NonNull java.lang.String mTaskName\nprivate @android.annotation.Nullable byte[] mResumptionToken\nclass TrainingExampleInputParcel extends java.lang.Object implements [android.os.Parcelable]\n@com.android.ondevicepersonalization.internal.util.DataClass(genAidl=false, genHiddenBuilder=true)")
@Deprecated
private void __metadata() {}
diff --git a/framework/java/android/adservices/ondevicepersonalization/TrainingExampleOutput.java b/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesOutput.java
index 1a698686..deea25cb 100644
--- a/framework/java/android/adservices/ondevicepersonalization/TrainingExampleOutput.java
+++ b/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesOutput.java
@@ -16,6 +16,9 @@
package android.adservices.ondevicepersonalization;
+import static android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import com.android.internal.util.Preconditions;
@@ -24,18 +27,14 @@ import com.android.ondevicepersonalization.internal.util.DataClass;
import java.util.Collections;
import java.util.List;
-import java.util.function.Consumer;
-/**
- * The output data of {@link IsolatedWorker#onTrainingExample(TrainingExampleInput, Consumer)}
- *
- * @hide
- */
+/** The output data of {@link IsolatedWorker#onTrainingExamples} */
+@FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
@DataClass(genBuilder = true, genEqualsHashCode = true)
-public final class TrainingExampleOutput {
+public final class TrainingExamplesOutput {
/**
- * A 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">
+ * A 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
@@ -43,52 +42,46 @@ public final class TrainingExampleOutput {
private List<byte[]> mTrainingExamples = Collections.emptyList();
/**
- * A list of resumption token byte arrays corresponding to training examples. The last
- * processed example's corresponding resumption token will be passed to
- * {@link IsolatedWorker#onTrainingExample(TrainingExampleInput, Consumer)} to support
- * resumption. The length of this list must match the length {@link #getTrainingExamples()}.
+ * A list of 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. The length of this list must match
+ * the length {@link #getTrainingExamples}.
*/
@NonNull
@DataClass.PluralOf("resumptionToken")
private List<byte[]> mResumptionTokens = Collections.emptyList();
-
private void onConstructed() {
Preconditions.checkArgument(mTrainingExamples.size() == mResumptionTokens.size());
}
-
-
// 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/TrainingExampleOutput.java
+ // $ codegen
+ // $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExampleOutput.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
+ // @formatter:off
@DataClass.Generated.Member
- /* package-private */ TrainingExampleOutput(
- @NonNull List<byte[]> trainingExamples,
- @NonNull List<byte[]> resumptionTokens) {
+ /* package-private */ TrainingExamplesOutput(
+ @NonNull List<byte[]> trainingExamples, @NonNull List<byte[]> resumptionTokens) {
this.mTrainingExamples = trainingExamples;
- AnnotationValidations.validate(
- NonNull.class, null, mTrainingExamples);
+ AnnotationValidations.validate(NonNull.class, null, mTrainingExamples);
this.mResumptionTokens = resumptionTokens;
- AnnotationValidations.validate(
- NonNull.class, null, mResumptionTokens);
+ AnnotationValidations.validate(NonNull.class, null, mResumptionTokens);
onConstructed();
}
/**
- * A 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">
+ * A 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
@@ -97,10 +90,10 @@ public final class TrainingExampleOutput {
}
/**
- * A list of resumption token byte arrays corresponding to training examples. The last
- * processed example's corresponding resumption token will be passed to
- * {@link IsolatedWorker#onTrainingExample(TrainingExampleInput, Consumer)} to support
- * resumption. The length of this list must match the length {@link #getTrainingExamples()}.
+ * A list of 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. The length of this list must match
+ * the length {@link #getTrainingExamples}.
*/
@DataClass.Generated.Member
public @NonNull List<byte[]> getResumptionTokens() {
@@ -117,7 +110,7 @@ public final class TrainingExampleOutput {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@SuppressWarnings("unchecked")
- TrainingExampleOutput that = (TrainingExampleOutput) o;
+ TrainingExamplesOutput that = (TrainingExamplesOutput) o;
//noinspection PointlessBooleanExpression
return true
&& java.util.Objects.equals(mTrainingExamples, that.mTrainingExamples)
@@ -136,9 +129,8 @@ public final class TrainingExampleOutput {
return _hash;
}
- /**
- * A builder for {@link TrainingExampleOutput}
- */
+ /** A builder for {@link TrainingExamplesOutput} */
+ @FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
@SuppressWarnings("WeakerAccess")
@DataClass.Generated.Member
public static final class Builder {
@@ -148,12 +140,11 @@ public final class TrainingExampleOutput {
private long mBuilderFieldsSet = 0L;
- public Builder() {
- }
+ public Builder() {}
/**
- * A 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">
+ * A 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
@@ -164,7 +155,9 @@ public final class TrainingExampleOutput {
return this;
}
- /** @see #setTrainingExamples */
+ /**
+ * @see #setTrainingExamples
+ */
@DataClass.Generated.Member
public @NonNull Builder addTrainingExample(@NonNull byte[] value) {
if (mTrainingExamples == null) setTrainingExamples(new java.util.ArrayList<>());
@@ -174,9 +167,9 @@ public final class TrainingExampleOutput {
/**
* A list of resumption token byte arrays corresponding to training examples. The last
- * processed example's corresponding resumption token will be passed to
- * {@link IsolatedWorker#onTrainingExample(TrainingExampleInput, Consumer)} to support
- * resumption. The length of this list must match the length {@link #getTrainingExamples()}.
+ * processed example's corresponding resumption token will be passed to {@link
+ * IsolatedWorker#onTrainingExamples} to support resumption. The length of this list must
+ * match the length {@link #getTrainingExamples}.
*/
@DataClass.Generated.Member
public @NonNull Builder setResumptionTokens(@NonNull List<byte[]> value) {
@@ -186,7 +179,9 @@ public final class TrainingExampleOutput {
return this;
}
- /** @see #setResumptionTokens */
+ /**
+ * @see #setResumptionTokens
+ */
@DataClass.Generated.Member
public @NonNull Builder addResumptionToken(@NonNull byte[] value) {
if (mResumptionTokens == null) setResumptionTokens(new java.util.ArrayList<>());
@@ -195,7 +190,7 @@ public final class TrainingExampleOutput {
}
/** Builds the instance. This builder should not be touched after calling this! */
- public @NonNull TrainingExampleOutput build() {
+ public @NonNull TrainingExamplesOutput build() {
checkNotUsed();
mBuilderFieldsSet |= 0x4; // Mark builder used
@@ -205,9 +200,8 @@ public final class TrainingExampleOutput {
if ((mBuilderFieldsSet & 0x2) == 0) {
mResumptionTokens = Collections.emptyList();
}
- TrainingExampleOutput o = new TrainingExampleOutput(
- mTrainingExamples,
- mResumptionTokens);
+ TrainingExamplesOutput o =
+ new TrainingExamplesOutput(mTrainingExamples, mResumptionTokens);
return o;
}
@@ -222,13 +216,14 @@ public final class TrainingExampleOutput {
@DataClass.Generated(
time = 1697575854959L,
codegenVersion = "1.0.23",
- sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExampleOutput.java",
- inputSignatures = "private @android.annotation.NonNull @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"trainingExample\") java.util.List<byte[]> mTrainingExamples\nprivate @android.annotation.NonNull @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"resumptionToken\") java.util.List<byte[]> mResumptionTokens\nprivate void onConstructed()\nclass TrainingExampleOutput extends java.lang.Object implements []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)")
+ sourceFile =
+ "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExampleOutput.java",
+ inputSignatures =
+ "private @android.annotation.NonNull @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"trainingExample\") java.util.List<byte[]> mTrainingExamples\nprivate @android.annotation.NonNull @com.android.ondevicepersonalization.internal.util.DataClass.PluralOf(\"resumptionToken\") java.util.List<byte[]> mResumptionTokens\nprivate void onConstructed()\nclass TrainingExampleOutput extends java.lang.Object implements []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
-
- //@formatter:on
+ // @formatter:on
// End of generated code
}
diff --git a/framework/java/android/adservices/ondevicepersonalization/TrainingExampleOutputParcel.java b/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesOutputParcel.java
index 8bbd518b..b620c098 100644
--- a/framework/java/android/adservices/ondevicepersonalization/TrainingExampleOutputParcel.java
+++ b/framework/java/android/adservices/ondevicepersonalization/TrainingExamplesOutputParcel.java
@@ -23,21 +23,17 @@ import com.android.ondevicepersonalization.internal.util.ByteArrayParceledListSl
import com.android.ondevicepersonalization.internal.util.DataClass;
/**
- * Parcelable version of {@link TrainingExampleOutput}
+ * Parcelable version of {@link TrainingExamplesOutput}
*
* @hide
*/
-@DataClass(genHiddenBuilder = true, genEqualsHashCode = true)
-public class TrainingExampleOutputParcel implements Parcelable {
+@DataClass(genAidl = false, genHiddenBuilder = true, genEqualsHashCode = true)
+public class TrainingExamplesOutputParcel implements Parcelable {
/** List of training examples */
- @Nullable
- ByteArrayParceledListSlice mTrainingExamples = null;
+ @Nullable ByteArrayParceledListSlice mTrainingExamples = null;
/** List of resumption tokens */
- @Nullable
- ByteArrayParceledListSlice mResumptionTokens = null;
-
-
+ @Nullable ByteArrayParceledListSlice mResumptionTokens = null;
// Code below generated by codegen v1.0.23.
//
@@ -45,15 +41,15 @@ public class TrainingExampleOutputParcel implements Parcelable {
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExampleOutputParcel.java
+ // $ codegen
+ // $ANDROID_BUILD_TOP/packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExampleOutputParcel.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
+ // @formatter:off
@DataClass.Generated.Member
- /* package-private */ TrainingExampleOutputParcel(
+ /* package-private */ TrainingExamplesOutputParcel(
@Nullable ByteArrayParceledListSlice trainingExamples,
@Nullable ByteArrayParceledListSlice resumptionTokens) {
this.mTrainingExamples = trainingExamples;
@@ -88,7 +84,7 @@ public class TrainingExampleOutputParcel implements Parcelable {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@SuppressWarnings("unchecked")
- TrainingExampleOutputParcel that = (TrainingExampleOutputParcel) o;
+ TrainingExamplesOutputParcel that = (TrainingExamplesOutputParcel) o;
//noinspection PointlessBooleanExpression
return true
&& java.util.Objects.equals(mTrainingExamples, that.mTrainingExamples)
@@ -128,13 +124,21 @@ public class TrainingExampleOutputParcel implements Parcelable {
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- protected TrainingExampleOutputParcel(@android.annotation.NonNull android.os.Parcel in) {
+ 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();
- ByteArrayParceledListSlice trainingExamples = (flg & 0x1) == 0 ? null : (ByteArrayParceledListSlice) in.readTypedObject(ByteArrayParceledListSlice.CREATOR);
- ByteArrayParceledListSlice resumptionTokens = (flg & 0x2) == 0 ? null : (ByteArrayParceledListSlice) in.readTypedObject(ByteArrayParceledListSlice.CREATOR);
+ ByteArrayParceledListSlice trainingExamples =
+ (flg & 0x1) == 0
+ ? null
+ : (ByteArrayParceledListSlice)
+ in.readTypedObject(ByteArrayParceledListSlice.CREATOR);
+ ByteArrayParceledListSlice resumptionTokens =
+ (flg & 0x2) == 0
+ ? null
+ : (ByteArrayParceledListSlice)
+ in.readTypedObject(ByteArrayParceledListSlice.CREATOR);
this.mTrainingExamples = trainingExamples;
this.mResumptionTokens = resumptionTokens;
@@ -143,21 +147,24 @@ public class TrainingExampleOutputParcel implements Parcelable {
}
@DataClass.Generated.Member
- public static final @android.annotation.NonNull Parcelable.Creator<TrainingExampleOutputParcel> CREATOR
- = new Parcelable.Creator<TrainingExampleOutputParcel>() {
- @Override
- public TrainingExampleOutputParcel[] newArray(int size) {
- return new TrainingExampleOutputParcel[size];
- }
-
- @Override
- public TrainingExampleOutputParcel createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
- return new TrainingExampleOutputParcel(in);
- }
- };
+ 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 TrainingExampleOutputParcel}
+ * A builder for {@link TrainingExamplesOutputParcel}
+ *
* @hide
*/
@SuppressWarnings("WeakerAccess")
@@ -195,7 +202,7 @@ public class TrainingExampleOutputParcel implements Parcelable {
}
/** Builds the instance. This builder should not be touched after calling this! */
- public @android.annotation.NonNull TrainingExampleOutputParcel build() {
+ public @android.annotation.NonNull TrainingExamplesOutputParcel build() {
checkNotUsed();
mBuilderFieldsSet |= 0x4; // Mark builder used
@@ -205,9 +212,8 @@ public class TrainingExampleOutputParcel implements Parcelable {
if ((mBuilderFieldsSet & 0x2) == 0) {
mResumptionTokens = null;
}
- TrainingExampleOutputParcel o = new TrainingExampleOutputParcel(
- mTrainingExamples,
- mResumptionTokens);
+ TrainingExamplesOutputParcel o =
+ new TrainingExamplesOutputParcel(mTrainingExamples, mResumptionTokens);
return o;
}
@@ -220,10 +226,10 @@ public class TrainingExampleOutputParcel implements Parcelable {
}
@DataClass.Generated(
- time = 1695743444776L,
+ time = 1699394212826L,
codegenVersion = "1.0.23",
sourceFile = "packages/modules/OnDevicePersonalization/framework/java/android/adservices/ondevicepersonalization/TrainingExampleOutputParcel.java",
- inputSignatures = " @android.annotation.Nullable com.android.ondevicepersonalization.internal.util.ByteArrayParceledListSlice mTrainingExamples\n @android.annotation.Nullable com.android.ondevicepersonalization.internal.util.ByteArrayParceledListSlice mResumptionTokens\nclass TrainingExampleOutputParcel extends java.lang.Object implements [android.os.Parcelable]\n@com.android.ondevicepersonalization.internal.util.DataClass(genHiddenBuilder=true, genEqualsHashCode=true)")
+ inputSignatures = " @android.annotation.Nullable com.android.ondevicepersonalization.internal.util.ByteArrayParceledListSlice mTrainingExamples\n @android.annotation.Nullable com.android.ondevicepersonalization.internal.util.ByteArrayParceledListSlice mResumptionTokens\nclass TrainingExampleOutputParcel 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() {}
diff --git a/framework/java/android/adservices/ondevicepersonalization/TrainingInterval.java b/framework/java/android/adservices/ondevicepersonalization/TrainingInterval.java
index eba12de1..890620e7 100644
--- a/framework/java/android/adservices/ondevicepersonalization/TrainingInterval.java
+++ b/framework/java/android/adservices/ondevicepersonalization/TrainingInterval.java
@@ -16,6 +16,9 @@
package android.adservices.ondevicepersonalization;
+import static android.adservices.ondevicepersonalization.Constants.KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import com.android.ondevicepersonalization.internal.util.AnnotationValidations;
@@ -23,37 +26,28 @@ import com.android.ondevicepersonalization.internal.util.DataClass;
import java.time.Duration;
-/**
- * Training interval settings required for federated computation jobs.
- *
- * @hide
- */
+/** Training interval settings required for federated computation jobs. */
+@FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
@DataClass(genBuilder = true, genHiddenConstDefs = true, genEqualsHashCode = true)
public final class TrainingInterval {
- /**
- * The scheduling mode for a one-off task.
- */
+ /** 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.
- */
+ /** 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.
+ * 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}. Only positive values are accepted, zero or negative
- * values will result in IllegalArgumentException.
+ * <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
@@ -62,26 +56,23 @@ public final class TrainingInterval {
*/
@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
+ // $ 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
-
+ // @formatter:off
/** @hide */
- @android.annotation.IntDef(prefix = "SCHEDULING_MODE_", value = {
- SCHEDULING_MODE_ONE_TIME,
- SCHEDULING_MODE_RECURRENT
- })
+ @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 {}
@@ -91,38 +82,43 @@ public final class TrainingInterval {
public static String schedulingModeToString(@SchedulingMode int value) {
switch (value) {
case SCHEDULING_MODE_ONE_TIME:
- return "SCHEDULING_MODE_ONE_TIME";
+ return "SCHEDULING_MODE_ONE_TIME";
case SCHEDULING_MODE_RECURRENT:
- return "SCHEDULING_MODE_RECURRENT";
- default: return Integer.toHexString(value);
+ return "SCHEDULING_MODE_RECURRENT";
+ default:
+ return Integer.toHexString(value);
}
}
@DataClass.Generated.Member
/* package-private */ TrainingInterval(
- @SchedulingMode int schedulingMode,
- @NonNull Duration minimumInterval) {
+ @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 + ")");
+ "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);
+ 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.
+ * 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() {
@@ -132,9 +128,9 @@ public final class TrainingInterval {
/**
* 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>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
@@ -175,9 +171,8 @@ public final class TrainingInterval {
return _hash;
}
- /**
- * A builder for {@link TrainingInterval}
- */
+ /** A builder for {@link TrainingInterval} */
+ @FlaggedApi(KEY_ENABLE_ONDEVICEPERSONALIZATION_APIS)
@SuppressWarnings("WeakerAccess")
@DataClass.Generated.Member
public static final class Builder {
@@ -187,13 +182,12 @@ public final class TrainingInterval {
private long mBuilderFieldsSet = 0L;
- public Builder() {
- }
+ 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.
+ * 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) {
@@ -206,9 +200,9 @@ public final class TrainingInterval {
/**
* 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>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
@@ -234,9 +228,7 @@ public final class TrainingInterval {
if ((mBuilderFieldsSet & 0x2) == 0) {
mMinimumInterval = Duration.ZERO;
}
- TrainingInterval o = new TrainingInterval(
- mSchedulingMode,
- mMinimumInterval);
+ TrainingInterval o = new TrainingInterval(mSchedulingMode, mMinimumInterval);
return o;
}
@@ -251,13 +243,14 @@ public final class TrainingInterval {
@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 []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
+ 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 []\n@com.android.ondevicepersonalization.internal.util.DataClass(genBuilder=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
-
- //@formatter:on
+ // @formatter:on
// End of generated code
}
diff --git a/src/com/android/ondevicepersonalization/services/display/DisplayHelper.java b/src/com/android/ondevicepersonalization/services/display/DisplayHelper.java
index b0aa954d..90a00847 100644
--- a/src/com/android/ondevicepersonalization/services/display/DisplayHelper.java
+++ b/src/com/android/ondevicepersonalization/services/display/DisplayHelper.java
@@ -16,7 +16,7 @@
package com.android.ondevicepersonalization.services.display;
-import android.adservices.ondevicepersonalization.RenderOutput;
+import android.adservices.ondevicepersonalization.RenderOutputParcel;
import android.adservices.ondevicepersonalization.RequestLogRecord;
import android.annotation.NonNull;
import android.content.Context;
@@ -27,6 +27,7 @@ import android.os.PersistableBundle;
import android.view.Display;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceControlViewHost.SurfacePackage;
+import android.view.WindowManager;
import android.webkit.WebSettings;
import android.webkit.WebView;
@@ -58,9 +59,9 @@ public class DisplayHelper {
mContext = context;
}
- /** Generates an HTML string from the template data in RenderOutput. */
+ /** Generates an HTML string from the template data in RenderOutputParcel. */
@NonNull public String generateHtml(
- @NonNull RenderOutput renderContentResult,
+ @NonNull RenderOutputParcel renderContentResult,
@NonNull String servicePackageName) {
// If htmlContent is provided, do not render the template.
String htmlContent = renderContentResult.getContent();
@@ -134,7 +135,12 @@ public class DisplayHelper {
@NonNull SettableFuture<SurfacePackage> resultFuture) {
try {
sLogger.d(TAG + ": createWebView() started");
- WebView webView = new WebView(mContext);
+ Display display = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
+ Context displayContext = mContext.createDisplayContext(display);
+ Context windowContext = displayContext.createWindowContext(
+ WindowManager.LayoutParams.TYPE_APPLICATION_PANEL, null);
+
+ WebView webView = new WebView(windowContext);
webView.setWebViewClient(
new OdpWebViewClient(mContext, servicePackageName, queryId, logRecord));
WebSettings webViewSettings = webView.getSettings();
@@ -143,8 +149,8 @@ public class DisplayHelper {
webViewSettings.setAllowContentAccess(false);
webView.loadData(html, "text/html; charset=utf-8", "UTF-8");
- Display display = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
- SurfaceControlViewHost host = new SurfaceControlViewHost(mContext, display, hostToken);
+ SurfaceControlViewHost host = new SurfaceControlViewHost(
+ windowContext, display, hostToken);
host.setView(webView, width, height);
SurfacePackage surfacePackage = host.getSurfacePackage();
sLogger.d(TAG + ": createWebView success: " + surfacePackage);
diff --git a/src/com/android/ondevicepersonalization/services/display/OdpWebViewClient.java b/src/com/android/ondevicepersonalization/services/display/OdpWebViewClient.java
index 0be485ef..d74f0c71 100644
--- a/src/com/android/ondevicepersonalization/services/display/OdpWebViewClient.java
+++ b/src/com/android/ondevicepersonalization/services/display/OdpWebViewClient.java
@@ -17,9 +17,9 @@
package com.android.ondevicepersonalization.services.display;
import android.adservices.ondevicepersonalization.Constants;
-import android.adservices.ondevicepersonalization.EventInput;
+import android.adservices.ondevicepersonalization.EventInputParcel;
import android.adservices.ondevicepersonalization.EventLogRecord;
-import android.adservices.ondevicepersonalization.EventOutput;
+import android.adservices.ondevicepersonalization.EventOutputParcel;
import android.adservices.ondevicepersonalization.RequestLogRecord;
import android.adservices.ondevicepersonalization.UserData;
import android.annotation.NonNull;
@@ -181,7 +181,7 @@ class OdpWebViewClient extends WebViewClient {
return true;
}
- private ListenableFuture<EventOutput> executeEventHandler(
+ private ListenableFuture<EventOutputParcel> executeEventHandler(
IsolatedServiceInfo isolatedServiceInfo,
EventUrlPayload payload) {
try {
@@ -192,7 +192,7 @@ class OdpWebViewClient extends WebViewClient {
/* includeEventData */ true);
serviceParams.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, binder);
// TODO(b/259950177): Add Query row to input.
- EventInput input = new EventInput.Builder()
+ EventInputParcel input = new EventInputParcel.Builder()
.setParameters(payload.getEventParams())
.setRequestLogRecord(mLogRecord)
.build();
@@ -213,7 +213,7 @@ class OdpWebViewClient extends WebViewClient {
result, isolatedServiceInfo.getStartTimeMillis(),
Constants.STATUS_SUCCESS);
return result.getParcelable(
- Constants.EXTRA_RESULT, EventOutput.class);
+ Constants.EXTRA_RESULT, EventOutputParcel.class);
},
mInjector.getExecutor())
.catchingAsync(
@@ -233,25 +233,25 @@ class OdpWebViewClient extends WebViewClient {
}
- ListenableFuture<EventOutput> getEventOutput(
+ ListenableFuture<EventOutputParcel> getEventOutputParcel(
ListenableFuture<IsolatedServiceInfo> loadFuture,
EventUrlPayload payload) {
try {
- sLogger.d(TAG + ": getEventOutput(): Starting isolated process.");
+ sLogger.d(TAG + ": getEventOutputParcel(): Starting isolated process.");
return FluentFuture.from(loadFuture)
.transformAsync(
result -> executeEventHandler(result, payload),
mInjector.getExecutor());
} catch (Exception e) {
- sLogger.e(TAG + ": getEventOutput() failed", e);
+ sLogger.e(TAG + ": getEventOutputParcel() failed", e);
return Futures.immediateFailedFuture(e);
}
}
- private ListenableFuture<Void> writeEvent(EventOutput result) {
+ private ListenableFuture<Void> writeEvent(EventOutputParcel result) {
try {
- sLogger.d(TAG + ": writeEvent() called. EventOutput: " + result.toString());
+ sLogger.d(TAG + ": writeEvent() called. EventOutputParcel: " + result.toString());
if (result == null || result.getEventLogRecord() == null) {
return Futures.immediateFuture(null);
}
@@ -293,7 +293,7 @@ class OdpWebViewClient extends WebViewClient {
mInjector.getProcessRunner().loadIsolatedService(
TASK_NAME, mServicePackageName);
- var doneFuture = FluentFuture.from(getEventOutput(loadFuture, eventUrlPayload))
+ var doneFuture = FluentFuture.from(getEventOutputParcel(loadFuture, eventUrlPayload))
.transformAsync(
result -> writeEvent(result),
mInjector.getExecutor())
diff --git a/src/com/android/ondevicepersonalization/services/download/OnDevicePersonalizationDataProcessingAsyncCallable.java b/src/com/android/ondevicepersonalization/services/download/OnDevicePersonalizationDataProcessingAsyncCallable.java
index 9156807b..b05527e4 100644
--- a/src/com/android/ondevicepersonalization/services/download/OnDevicePersonalizationDataProcessingAsyncCallable.java
+++ b/src/com/android/ondevicepersonalization/services/download/OnDevicePersonalizationDataProcessingAsyncCallable.java
@@ -17,7 +17,7 @@
package com.android.ondevicepersonalization.services.download;
import android.adservices.ondevicepersonalization.Constants;
-import android.adservices.ondevicepersonalization.DownloadCompletedOutput;
+import android.adservices.ondevicepersonalization.DownloadCompletedOutputParcel;
import android.adservices.ondevicepersonalization.DownloadInputParcel;
import android.adservices.ondevicepersonalization.UserData;
import android.content.Context;
@@ -236,8 +236,8 @@ public class OnDevicePersonalizationDataProcessingAsyncCallable implements Async
Map<String, VendorData> vendorDataMap) {
sLogger.d(TAG + ": Plugin filter code completed successfully");
List<VendorData> filteredList = new ArrayList<>();
- DownloadCompletedOutput downloadResult = pluginResult.getParcelable(
- Constants.EXTRA_RESULT, DownloadCompletedOutput.class);
+ DownloadCompletedOutputParcel downloadResult = pluginResult.getParcelable(
+ Constants.EXTRA_RESULT, DownloadCompletedOutputParcel.class);
List<String> retainedKeys = downloadResult.getRetainedKeys();
if (retainedKeys == null) {
// TODO(b/270710021): Determine how to correctly handle null retainedKeys.
diff --git a/src/com/android/ondevicepersonalization/services/federatedcompute/OdpExampleStoreService.java b/src/com/android/ondevicepersonalization/services/federatedcompute/OdpExampleStoreService.java
index b0ae9bd4..5d63c651 100644
--- a/src/com/android/ondevicepersonalization/services/federatedcompute/OdpExampleStoreService.java
+++ b/src/com/android/ondevicepersonalization/services/federatedcompute/OdpExampleStoreService.java
@@ -17,8 +17,8 @@
package com.android.ondevicepersonalization.services.federatedcompute;
import android.adservices.ondevicepersonalization.Constants;
-import android.adservices.ondevicepersonalization.TrainingExampleInput;
-import android.adservices.ondevicepersonalization.TrainingExampleOutputParcel;
+import android.adservices.ondevicepersonalization.TrainingExamplesInputParcel;
+import android.adservices.ondevicepersonalization.TrainingExamplesOutputParcel;
import android.adservices.ondevicepersonalization.UserData;
import android.annotation.NonNull;
import android.content.Context;
@@ -146,8 +146,8 @@ public final class OdpExampleStoreService extends ExampleStoreService {
resumptionToken = eventState.getToken();
}
- TrainingExampleInput input =
- new TrainingExampleInput.Builder()
+ TrainingExamplesInputParcel input =
+ new TrainingExamplesInputParcel.Builder()
.setResumptionToken(resumptionToken)
.setPopulationName(populationName)
.setTaskName(taskName)
@@ -155,16 +155,16 @@ public final class OdpExampleStoreService extends ExampleStoreService {
ListenableFuture<IsolatedServiceInfo> loadFuture =
mInjector.getProcessRunner().loadIsolatedService(TASK_NAME, packageName);
- ListenableFuture<TrainingExampleOutputParcel> resultFuture =
+ ListenableFuture<TrainingExamplesOutputParcel> resultFuture =
FluentFuture.from(loadFuture)
.transformAsync(
- result -> executeOnTrainingExample(result, input, packageName),
+ result -> executeOnTrainingExamples(result, input, packageName),
OnDevicePersonalizationExecutors.getBackgroundExecutor())
.transform(
result -> {
return result.getParcelable(
Constants.EXTRA_RESULT,
- TrainingExampleOutputParcel.class);
+ TrainingExamplesOutputParcel.class);
},
OnDevicePersonalizationExecutors.getBackgroundExecutor())
.withTimeout(
@@ -174,14 +174,14 @@ public final class OdpExampleStoreService extends ExampleStoreService {
Futures.addCallback(
resultFuture,
- new FutureCallback<TrainingExampleOutputParcel>() {
+ new FutureCallback<TrainingExamplesOutputParcel>() {
@Override
public void onSuccess(
- TrainingExampleOutputParcel trainingExampleOutputParcel) {
+ TrainingExamplesOutputParcel trainingExamplesOutputParcel) {
ByteArrayParceledListSlice trainingExamplesListSlice =
- trainingExampleOutputParcel.getTrainingExamples();
+ trainingExamplesOutputParcel.getTrainingExamples();
ByteArrayParceledListSlice resumptionTokensListSlice =
- trainingExampleOutputParcel.getResumptionTokens();
+ trainingExamplesOutputParcel.getResumptionTokens();
if (trainingExamplesListSlice == null
|| resumptionTokensListSlice == null) {
callback.onStartQuerySuccess(
@@ -219,11 +219,11 @@ public final class OdpExampleStoreService extends ExampleStoreService {
}
}
- private ListenableFuture<Bundle> executeOnTrainingExample(
+ private ListenableFuture<Bundle> executeOnTrainingExamples(
IsolatedServiceInfo isolatedServiceInfo,
- TrainingExampleInput exampleInput,
+ TrainingExamplesInputParcel exampleInput,
String packageName) {
- sLogger.d(TAG + ": executeOnTrainingExample() started.");
+ sLogger.d(TAG + ": executeOnTrainingExamples() started.");
Bundle serviceParams = new Bundle();
serviceParams.putParcelable(Constants.EXTRA_INPUT, exampleInput);
DataAccessServiceImpl binder =
diff --git a/src/com/android/ondevicepersonalization/services/request/AppRequestFlow.java b/src/com/android/ondevicepersonalization/services/request/AppRequestFlow.java
index b5f6288a..88d94214 100644
--- a/src/com/android/ondevicepersonalization/services/request/AppRequestFlow.java
+++ b/src/com/android/ondevicepersonalization/services/request/AppRequestFlow.java
@@ -18,8 +18,8 @@ package com.android.ondevicepersonalization.services.request;
import android.adservices.ondevicepersonalization.Constants;
import android.adservices.ondevicepersonalization.EventLogRecord;
-import android.adservices.ondevicepersonalization.ExecuteInput;
-import android.adservices.ondevicepersonalization.ExecuteOutput;
+import android.adservices.ondevicepersonalization.ExecuteInputParcel;
+import android.adservices.ondevicepersonalization.ExecuteOutputParcel;
import android.adservices.ondevicepersonalization.RenderingConfig;
import android.adservices.ondevicepersonalization.RequestLogRecord;
import android.adservices.ondevicepersonalization.UserData;
@@ -178,7 +178,7 @@ public class AppRequestFlow {
ListenableFuture<IsolatedServiceInfo> loadFuture =
mInjector.getProcessRunner().loadIsolatedService(
TASK_NAME, mService.getPackageName());
- ListenableFuture<ExecuteOutput> resultFuture = FluentFuture.from(loadFuture)
+ ListenableFuture<ExecuteOutputParcel> resultFuture = FluentFuture.from(loadFuture)
.transformAsync(
result -> executeAppRequest(result),
mInjector.getExecutor()
@@ -186,7 +186,7 @@ public class AppRequestFlow {
.transform(
result -> {
return result.getParcelable(
- Constants.EXTRA_RESULT, ExecuteOutput.class);
+ Constants.EXTRA_RESULT, ExecuteOutputParcel.class);
},
mInjector.getExecutor()
);
@@ -239,8 +239,8 @@ public class AppRequestFlow {
IsolatedServiceInfo isolatedServiceInfo) {
sLogger.d(TAG + ": executeAppRequest() started.");
Bundle serviceParams = new Bundle();
- ExecuteInput input =
- new ExecuteInput.Builder()
+ ExecuteInputParcel input =
+ new ExecuteInputParcel.Builder()
.setAppPackageName(mCallingPackageName)
.setAppParams(mParams)
.build();
@@ -279,7 +279,7 @@ public class AppRequestFlow {
);
}
- private ListenableFuture<Long> logQuery(ExecuteOutput result) {
+ private ListenableFuture<Long> logQuery(ExecuteOutputParcel result) {
sLogger.d(TAG + ": logQuery() started.");
EventsDao eventsDao = EventsDao.getInstance(mContext);
// Insert query
@@ -335,11 +335,11 @@ public class AppRequestFlow {
}
private ListenableFuture<List<String>> createTokens(
- ListenableFuture<ExecuteOutput> resultFuture,
+ ListenableFuture<ExecuteOutputParcel> resultFuture,
ListenableFuture<Long> queryIdFuture) {
try {
sLogger.d(TAG + ": createTokens() started.");
- ExecuteOutput result = Futures.getDone(resultFuture);
+ ExecuteOutputParcel result = Futures.getDone(resultFuture);
long queryId = Futures.getDone(queryIdFuture);
List<RenderingConfig> renderingConfigs = result.getRenderingConfigs();
Objects.requireNonNull(renderingConfigs);
diff --git a/src/com/android/ondevicepersonalization/services/request/RenderFlow.java b/src/com/android/ondevicepersonalization/services/request/RenderFlow.java
index 6ba342ed..d88e67a5 100644
--- a/src/com/android/ondevicepersonalization/services/request/RenderFlow.java
+++ b/src/com/android/ondevicepersonalization/services/request/RenderFlow.java
@@ -17,8 +17,8 @@
package com.android.ondevicepersonalization.services.request;
import android.adservices.ondevicepersonalization.Constants;
-import android.adservices.ondevicepersonalization.RenderInput;
-import android.adservices.ondevicepersonalization.RenderOutput;
+import android.adservices.ondevicepersonalization.RenderInputParcel;
+import android.adservices.ondevicepersonalization.RenderOutputParcel;
import android.adservices.ondevicepersonalization.RenderingConfig;
import android.adservices.ondevicepersonalization.RequestLogRecord;
import android.adservices.ondevicepersonalization.aidl.IRequestSurfacePackageCallback;
@@ -229,7 +229,7 @@ public class RenderFlow {
mInjector.getExecutor())
.transform(result -> {
return result.getParcelable(
- Constants.EXTRA_RESULT, RenderOutput.class);
+ Constants.EXTRA_RESULT, RenderOutputParcel.class);
}, mInjector.getExecutor())
.transform(
result -> mDisplayHelper.generateHtml(result, mServicePackageName),
@@ -255,8 +255,8 @@ public class RenderFlow {
RenderingConfig renderingConfig) {
sLogger.d(TAG + "executeRenderContentRequest() started.");
Bundle serviceParams = new Bundle();
- RenderInput input =
- new RenderInput.Builder()
+ RenderInputParcel input =
+ new RenderInputParcel.Builder()
.setHeight(mHeight)
.setWidth(mWidth)
.setRenderingConfigIndex(slotIndex)
diff --git a/tests/endtoendtests/OdpClient/AndroidManifest.xml b/tests/endtoendtests/OdpClient/AndroidManifest.xml
index 71025450..1af14085 100644
--- a/tests/endtoendtests/OdpClient/AndroidManifest.xml
+++ b/tests/endtoendtests/OdpClient/AndroidManifest.xml
@@ -17,13 +17,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.odpclient"
- android:versionName="1.0.2" >
+ android:versionName="1.0.3" >
<uses-permission android:name="android.permission.ondevicepersonalization.MODIFY_ONDEVICEPERSONALIZATION_STATE"/>
<application
android:label="@string/title_activity_main">
<activity
android:name="com.example.odpclient.MainActivity"
+ android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/tests/endtoendtests/OdpClient/src/com/example/odpclient/MainActivity.java b/tests/endtoendtests/OdpClient/src/com/example/odpclient/MainActivity.java
index 2fe34124..7e445d89 100644
--- a/tests/endtoendtests/OdpClient/src/com/example/odpclient/MainActivity.java
+++ b/tests/endtoendtests/OdpClient/src/com/example/odpclient/MainActivity.java
@@ -22,6 +22,7 @@ import android.adservices.ondevicepersonalization.SurfacePackageToken;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -29,6 +30,7 @@ import android.os.OutcomeReceiver;
import android.os.PersistableBundle;
import android.util.Log;
import android.view.SurfaceControlViewHost.SurfacePackage;
+import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
@@ -45,7 +47,6 @@ import java.util.concurrent.atomic.AtomicReference;
public class MainActivity extends Activity {
private static final String TAG = "OdpClient";
- private OnDevicePersonalizationManager mOdpManager = null;
private OnDevicePersonalizationConfigManager mOdpConfigManager = null;
private EditText mTextBox;
@@ -59,20 +60,32 @@ public class MainActivity extends Activity {
private Context mContext;
private static Executor sCallbackExecutor = Executors.newSingleThreadExecutor();
+ class SurfaceCallback implements SurfaceHolder.Callback {
+ @Override public void surfaceCreated(SurfaceHolder holder) {
+ Log.d(TAG, "surfaceCreated");
+ }
+ @Override public void surfaceDestroyed(SurfaceHolder holder) {
+ Log.d(TAG, "surfaceDestroyed");
+ }
+ @Override public void surfaceChanged(
+ SurfaceHolder holder, int format, int width, int height) {
+ Log.d(TAG, "surfaceChanged");
+ }
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
+ Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = getApplicationContext();
- if (mOdpManager == null) {
- mOdpManager = mContext.getSystemService(OnDevicePersonalizationManager.class);
- }
if (mOdpConfigManager == null) {
mOdpConfigManager = mContext.getSystemService(
OnDevicePersonalizationConfigManager.class);
}
mRenderedView = findViewById(R.id.rendered_view);
mRenderedView.setVisibility(View.INVISIBLE);
+ mRenderedView.getHolder().addCallback(new SurfaceCallback());
mGetAdButton = findViewById(R.id.get_ad_button);
mScheduleTrainingButton = findViewById(R.id.schedule_training_button);
mSetStatusButton = findViewById(R.id.set_status_button);
@@ -99,18 +112,19 @@ public class MainActivity extends Activity {
mReportConversionButton.setOnClickListener(v -> reportConversion());
}
+ private OnDevicePersonalizationManager getOdpManager() {
+ return mContext.getSystemService(OnDevicePersonalizationManager.class);
+ }
+
private void makeRequest() {
try {
- if (mOdpManager == null) {
- makeToast("OnDevicePersonalizationManager is null");
- return;
- }
+ var odpManager = getOdpManager();
CountDownLatch latch = new CountDownLatch(1);
Log.i(TAG, "Starting execute()");
AtomicReference<SurfacePackageToken> slotResultHandle = new AtomicReference<>();
PersistableBundle appParams = new PersistableBundle();
appParams.putString("keyword", mTextBox.getText().toString());
- mOdpManager.execute(
+ odpManager.execute(
ComponentName.createRelative(
"com.example.odpsamplenetwork",
"com.example.odpsamplenetwork.SampleService"),
@@ -136,7 +150,7 @@ public class MainActivity extends Activity {
});
latch.await();
Log.d(TAG, "wait success");
- mOdpManager.requestSurfacePackage(
+ odpManager.requestSurfacePackage(
slotResultHandle.get(),
mRenderedView.getHostToken(),
getDisplay().getDisplayId(),
@@ -176,15 +190,12 @@ public class MainActivity extends Activity {
private void scheduleTraining() {
try {
- if (mOdpManager == null) {
- makeToast("OnDevicePersonalizationManager is null");
- return;
- }
+ var odpManager = getOdpManager();
CountDownLatch latch = new CountDownLatch(1);
Log.i(TAG, "Starting execute()");
PersistableBundle appParams = new PersistableBundle();
appParams.putString("schedule_training", mScheduleTrainingTextBox.getText().toString());
- mOdpManager.execute(
+ odpManager.execute(
ComponentName.createRelative(
"com.example.odpsamplenetwork",
"com.example.odpsamplenetwork.SampleService"),
@@ -211,15 +222,12 @@ public class MainActivity extends Activity {
private void reportConversion() {
try {
- if (mOdpManager == null) {
- makeToast("OnDevicePersonalizationManager is null");
- return;
- }
+ var odpManager = getOdpManager();
CountDownLatch latch = new CountDownLatch(1);
Log.i(TAG, "Starting execute()");
PersistableBundle appParams = new PersistableBundle();
appParams.putString("conversion_ad_id", mReportConversionTextBox.getText().toString());
- mOdpManager.execute(
+ odpManager.execute(
ComponentName.createRelative(
"com.example.odpsamplenetwork",
"com.example.odpsamplenetwork.SampleService"),
@@ -268,4 +276,39 @@ public class MainActivity extends Activity {
Log.i(TAG, message);
runOnUiThread(() -> Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show());
}
+
+ @Override
+ public void onPause() {
+ Log.d(TAG, "onPause");
+ super.onPause();
+ }
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ Log.d(TAG, "onSaveInstanceState");
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.d(TAG, "onDestroy");
+ super.onDestroy();
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ Log.d(TAG, "onRestoreInstanceState");
+ super.onRestoreInstanceState(savedInstanceState);
+ }
+
+ @Override
+ public void onResume() {
+ Log.d(TAG, "onResume");
+ super.onResume();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ Log.d(TAG, "onConfigurationChanged");
+ super.onConfigurationChanged(newConfig);
+ }
}
diff --git a/tests/endtoendtests/OdpSampleNetwork/AndroidManifest.xml b/tests/endtoendtests/OdpSampleNetwork/AndroidManifest.xml
index 3dce1e63..0d98ec4f 100644
--- a/tests/endtoendtests/OdpSampleNetwork/AndroidManifest.xml
+++ b/tests/endtoendtests/OdpSampleNetwork/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.odpsamplenetwork"
- android:versionName="1.0.2" >
+ android:versionName="1.0.3" >
<application android:label="OdpSampleNetwork"
android:debuggable="true">
<property android:name="android.ondevicepersonalization.ON_DEVICE_PERSONALIZATION_CONFIG"
diff --git a/tests/endtoendtests/OdpSampleNetwork/res/raw/test_data1.json b/tests/endtoendtests/OdpSampleNetwork/res/raw/test_data1.json
index 6cd2a147..c704637b 100644
--- a/tests/endtoendtests/OdpSampleNetwork/res/raw/test_data1.json
+++ b/tests/endtoendtests/OdpSampleNetwork/res/raw/test_data1.json
@@ -6,6 +6,106 @@
},
{ "key": "template1",
"data": "<img src=\"$impressionUrl\">\n<a href=\"$clickUrl\">${adText}!</a>"
- }
+ },
+ {"key": "example1", "data": "0,1,1,5,0,1382,4,15,2,181,1,2,,2,68fd1e64,80e26c9b,fb936136,7b4723c4,25c83c98,7e0ccccf,de7995b8,1f89b562,a73ee510,a8cd5504,b2cb9c98,37c9c164,2824a5f6,1adce6ef,8ba8b39a,891b62e7,e5ba7672,f54016b9,21ddcdc9,b1252a9d,07b5194c,,3a171ecb,c5c50484,e8b83407,9727dd16,"},
+ {"key": "example2", "data": "0,2,0,44,1,102,8,2,2,4,1,1,,4,68fd1e64,f0cf0024,6f67f7e5,41274cd7,25c83c98,fe6b92e5,922afcc0,0b153874,a73ee510,2b53e5fb,4f1b46f3,623049e6,d7020589,b28479f6,e6c5b5cd,c92f3b61,07c540c4,b04e4670,21ddcdc9,5840adea,60f6221e,,3a171ecb,43f13e8b,e8b83407,731c3655,"},
+ {"key": "example3", "data": "0,2,0,1,14,767,89,4,2,245,1,3,3,45,287e684f,0a519c5c,02cf9876,c18be181,25c83c98,7e0ccccf,c78204a1,0b153874,a73ee510,3b08e48b,5f5e6091,8fe001f4,aa655a2f,07d13a8f,6dc710ed,36103458,8efede7f,3412118d,,,e587c466,ad3062eb,3a171ecb,3b183c5c,,,"},
+ {"key": "example4", "data": "0,,893,,,4392,,0,0,0,,0,,,68fd1e64,2c16a946,a9a87e68,2e17d6f6,25c83c98,fe6b92e5,2e8a689b,0b153874,a73ee510,efea433b,e51ddf94,a30567ca,3516f6e6,07d13a8f,18231224,52b8680f,1e88c74f,74ef3502,,,6b3a5ca6,,3a171ecb,9117a34a,,,"},
+ {"key": "example5", "data": "0,3,-1,,0,2,0,3,0,0,1,1,,0,8cf07265,ae46a29d,c81688bb,f922efad,25c83c98,13718bbd,ad9fa255,0b153874,a73ee510,5282c137,e5d8af57,66a76a26,f06c53ac,1adce6ef,8ff4b403,01adbab4,1e88c74f,26b3c7a7,,,21c9516a,,32c7478e,b34f3128,,,"},
+ {"key": "example6", "data": "0,,-1,,,12824,,0,0,6,,0,,,05db9164,6c9c9cf3,2730ec9c,5400db8b,43b19349,6f6d9be8,53b5f978,0b153874,a73ee510,3b08e48b,91e8fc27,be45b877,9ff13f22,07d13a8f,06969a20,9bc7fff5,776ce399,92555263,,,242bb710,8ec974f4,be7c41b4,72c78f11,,,"},
+ {"key": "example7", "data": "0,,1,2,,3168,,0,1,2,,0,,,439a44a4,ad4527a2,c02372d0,d34ebbaa,43b19349,fe6b92e5,4bc6ffea,0b153874,a73ee510,3b08e48b,a4609aab,14d63538,772a00d7,07d13a8f,f9d1382e,b00d3dc9,776ce399,cdfa8259,,,20062612,,93bad2c0,1b256e61,,,"},
+ {"key": "example8", "data": "1,1,4,2,0,0,0,1,0,0,1,1,,0,68fd1e64,2c16a946,503b9dbc,e4dbea90,f3474129,13718bbd,38eb9cf4,1f89b562,a73ee510,547c0ffe,bc8c9f21,60ab2f07,46f42a63,07d13a8f,18231224,e6b6bdc7,e5ba7672,74ef3502,,,5316a17f,,32c7478e,9117a34a,,,"},
+ {"key": "example9", "data": "0,,44,4,8,19010,249,28,31,141,,1,,8,05db9164,d833535f,d032c263,c18be181,25c83c98,7e0ccccf,d5b6acf2,0b153874,a73ee510,2acdcf4e,086ac2d2,dfbb09fb,41a6ae00,b28479f6,e2502ec9,84898b2a,e5ba7672,42a2edb9,,,0014c32a,,32c7478e,3b183c5c,,,"},
+ {"key": "example10", "data": "0,,35,,1,33737,21,1,2,3,,1,,1,05db9164,510b40a5,d03e7c24,eb1fd928,25c83c98,,52283d1c,0b153874,a73ee510,015ac893,e51ddf94,951fe4a9,3516f6e6,07d13a8f,2ae4121c,8ec71479,d4bb7bd8,70d0f5f9,,,0e63fca0,,32c7478e,0e8fe315,,,"},
+ {"key": "example11", "data": "0,,2,632,0,56770,,0,5,65,,0,,2,05db9164,0468d672,7ae80d0f,80d8555a,25c83c98,7e0ccccf,04277bf9,0b153874,7cc72ec2,3b08e48b,7e2c5c15,cfc86806,91a1b611,b28479f6,58251aab,146a70fd,776ce399,0b331314,21ddcdc9,5840adea,cbec39db,,3a171ecb,cedad179,ea9a246c,9a556cfc,"},
+ {"key": "example12", "data": "0,0,6,6,6,421,109,1,7,107,0,1,,6,05db9164,9b5fd12f,,,4cf72387,,111121f4,0b153874,a73ee510,3b08e48b,ac9c2e8f,,6e2d6a15,07d13a8f,796a1a2e,,d4bb7bd8,8aaa5b67,,,,,32c7478e,,,,"},
+ {"key": "example13", "data": "1,0,-1,,,1465,0,17,0,4,0,4,,,241546e0,38a947a1,fa673455,6a14f9b9,25c83c98,fe6b92e5,1c86e0eb,1f89b562,a73ee510,e7ba2569,755e4a50,208d9687,5978055e,07d13a8f,5182f694,f8b34416,e5ba7672,e5f8f18f,,,f3ddd519,,32c7478e,b34f3128,,,"},
+ {"key": "example14", "data": "1,,2,11,5,10262,34,2,4,5,,1,,5,be589b51,287130e0,cd7a7a22,fb7334df,25c83c98,,6cdb3998,361384ce,a73ee510,3ff10fb2,5874c9c9,976cbd4c,740c210d,1adce6ef,310d155b,07eb8110,07c540c4,891589e7,18259a83,a458ea53,a0ab60ca,,32c7478e,a052b1ed,9b3e8820,8967c0d2,"},
+ {"key": "example15", "data": "0,0,51,84,4,3633,26,1,4,8,0,1,,4,5a9ed9b0,80e26c9b,97144401,5dbf0cc5,0942e0a7,13718bbd,9ce6136d,0b153874,a73ee510,2106e595,b5bb9d63,04f55317,ab04d8fe,1adce6ef,0ad47a49,2bd32e5c,3486227d,12195b22,21ddcdc9,b1252a9d,fa131867,,dbb486d7,8ecc176a,e8b83407,c43c3f58,"},
+ {"key": "example16", "data": "0,,2,1,18,20255,,0,1,1306,,0,,20,05db9164,bc6e3dc1,67799c69,d00d0f35,4cf72387,7e0ccccf,ca4fd8f8,64523cfa,a73ee510,3b08e48b,a0060bca,b9f28c33,22d23aac,5aebfb83,d702713a,0f655650,776ce399,3a2028fd,,,b426bc93,,3a171ecb,2e0a0035,,,"},
+ {"key": "example17", "data": "1,1,987,,2,105,2,1,2,2,1,1,,2,68fd1e64,38d50e09,da603082,431a5096,43b19349,7e0ccccf,3f35b640,0b153874,a73ee510,3b08e48b,3d5fb018,6aaab577,94172618,07d13a8f,ee569ce2,2f03ef40,d4bb7bd8,582152eb,21ddcdc9,b1252a9d,3b203ca1,,32c7478e,b21dc903,001f3601,aa5f0a15,"},
+ {"key": "example18", "data": "0,0,1,,0,16597,557,3,5,123,0,1,,1,8cf07265,7cd19acc,77f2f2e5,d16679b9,4cf72387,fbad5c96,8fb24933,0b153874,a73ee510,0095a535,3617b5f5,9f32b866,428332cf,b28479f6,83ebd498,31ca40b6,e5ba7672,d0e5eb07,,,dfcfc3fa,ad3062eb,32c7478e,aee52b6f,,,"},
+ {"key": "example19", "data": "0,0,24,4,2,2056,12,6,10,83,0,1,,2,05db9164,f0cf0024,08b45d8b,cbb5af1b,384874ce,fbad5c96,81bb0302,37e4aa92,a73ee510,175d6c71,b7094596,1c547463,1f9d2c38,1adce6ef,55dc357b,0ca69655,e5ba7672,b04e4670,21ddcdc9,b1252a9d,f3caefdd,,32c7478e,4c8e5aef,ea9a246c,9593bba9,"},
+ {"key": "example20", "data": "0,7,102,,3,780,15,7,15,15,1,1,,3,3c9d8785,b0660259,3a960356,15c92ddb,4cf72387,13718bbd,00c46cd1,0b153874,a73ee510,62cfc6bd,8cffe207,656e5413,ff5626de,ad1cc976,27b1230c,fa8d05aa,e5ba7672,5edd90de,,,e12ce348,,c3dc6cef,49045073,,,"},
+ {"key": "example21", "data": "1,,47,,0,6399,38,19,10,143,,10,,6,1464facd,38a947a1,223b0e16,ca55061c,25c83c98,7e0ccccf,6933dec1,5b392875,a73ee510,3b08e48b,860c302b,156f99ef,30735474,1adce6ef,0e78291e,5fbf4a84,e5ba7672,1999bae9,,,deb9605d,,32c7478e,e448275f,,,"},
+ {"key": "example22", "data": "0,0,1,80,0,1848,287,1,4,46,0,1,,4,05db9164,09e68b86,13b87f72,13a91973,25c83c98,7e0ccccf,cc5ed2f1,0b153874,a73ee510,3b08e48b,081c279a,d25f00b6,9f16a973,07d13a8f,36721ddc,1746d357,d4bb7bd8,5aed7436,a153cea2,a458ea53,dd37e0d1,,32c7478e,c70a58f2,e8b83407,af7ece63,"},
+ {"key": "example23", "data": "0,,0,14,6,7132,171,2,2,6,,1,,6,05db9164,38a947a1,e88a1d4c,8eb9aec7,25c83c98,fbad5c96,3fd38f3b,5b392875,a73ee510,5162b19c,7c430b79,4ac05ba7,7f0d7407,b28479f6,d1128331,ce881087,07c540c4,5d93f8ab,,,57d0811b,,3a171ecb,1793a828,,,"},
+ {"key": "example24", "data": "0,,9,9,17,11774,,0,23,128,,0,,17,05db9164,08d6d899,cf59444f,60d5f5a7,25c83c98,7e0ccccf,38850d41,0b153874,a73ee510,6e7947ce,49aeb6a9,1d00cbc4,8f7e5dc7,07d13a8f,41f10449,b93ac0ad,1e88c74f,698d1c68,,,bf8efd4c,,c7dc6720,f96a556f,,,"},
+ {"key": "example25", "data": "0,0,1,2,,6190,84,1,27,71,0,1,,,5a9ed9b0,3df44d94,d032c263,c18be181,25c83c98,7e0ccccf,a0845add,0b153874,a73ee510,967857d1,e469acef,dfbb09fb,849a0a56,07d13a8f,72d05a1c,84898b2a,d4bb7bd8,e7648a8f,,,0014c32a,c9d4222a,3a171ecb,3b183c5c,,,"},
+ {"key": "example26", "data": "0,,4,16,,5925,2,2,0,0,,1,,,5a9ed9b0,09e68b86,64094ddd,b0a4d1e3,25c83c98,,b87f4a4a,0b153874,a73ee510,2124a520,319687c9,b51dc799,62036f49,64c94865,91126f30,1a00d73c,07c540c4,5aed7436,aa0cf899,a458ea53,c30dce78,,32c7478e,3fdb382b,e8b83407,49d68486,"},
+ {"key": "example27", "data": "1,0,1,20,16,1548,93,42,32,912,0,15,1,16,8cf07265,942f9a8d,a8e40bcf,0365276a,25c83c98,7e0ccccf,3f4ec687,1f89b562,a73ee510,726f00fd,c4adf918,27c604a6,85dbe138,07d13a8f,a8e962af,c449f783,27c07bd6,1f868fdd,21ddcdc9,a458ea53,7eee76d1,,32c7478e,9af06ad9,9d93af03,cdfe5ab7,"},
+ {"key": "example28", "data": "1,0,20,2,2,7188,170,2,3,24,0,2,0,2,68fd1e64,38a947a1,ee6e4611,30d9fc77,4cf72387,7e0ccccf,bf9d4f90,0b153874,a73ee510,b7c4dad5,81cae03e,5332e3fb,d413ef3e,07d13a8f,a6d97bf2,ec676ace,3486227d,02e8d897,,,b055c31b,,3a171ecb,ae2cd100,,,"},
+ {"key": "example29", "data": "1,0,78,2,15,4311,85,4,18,230,0,3,,15,68fd1e64,1287a654,5ed035c9,5b5365b2,4cf72387,6f6d9be8,1b1aa9ea,0b153874,a73ee510,c3e69838,7a3651f5,df8b1dea,95bc260c,b28479f6,ced5be3a,4cc0abe4,e5ba7672,df00d249,,,f520f961,,32c7478e,27b60b01,,,"},
+ {"key": "example30", "data": "1,3,0,4,13,224,28,3,35,27,1,1,,13,05db9164,90081f33,993f507e,14a74146,25c83c98,13718bbd,dc7659bd,0b153874,a73ee510,03e48276,e51ddf94,18fe7085,3516f6e6,64c94865,98995c3b,8c48eb08,e5ba7672,7181ccc8,,,2ed6b316,,3a171ecb,abf08f1b,,,"},
+ {"key": "example31", "data": "1,,277,,3,7318,24,6,3,98,,1,,3,8cf07265,9adf4cf9,2e76fb61,0b1ad9da,4cf72387,fe6b92e5,75dcaaca,0b153874,a73ee510,3b08e48b,8aabdae8,9886a0a7,edcf17ce,07d13a8f,2aaebd23,338c0d09,e5ba7672,c7dbecd5,,,60d2d691,,3a171ecb,90b6276f,,,"},
+ {"key": "example32", "data": "0,,-1,,,4956,,0,37,97,,0,,,be589b51,4c2bc594,d032c263,c18be181,25c83c98,fe6b92e5,aa0d873c,0b153874,a73ee510,3b08e48b,868744ab,dfbb09fb,9dfda2b9,8ceecbc8,7ac43a46,84898b2a,776ce399,bc48b783,,,0014c32a,,55dd3565,3b183c5c,,,"},
+ {"key": "example33", "data": "0,1,0,1,,1427,3,16,11,50,0,2,1,,05db9164,26a88120,615e3e4e,2788fed8,4cf72387,7e0ccccf,3f4ec687,0b153874,a73ee510,0e9ead52,c4adf918,f5d19c1c,85dbe138,07d13a8f,24ff9452,1034ac0d,3486227d,b486119d,,,63580fba,,32c7478e,2a90c749,,,"},
+ {"key": "example34", "data": "0,4,0,55,8,859,13,4,12,13,1,1,,8,05db9164,e5fb1af3,4b644986,5dbf0cc5,25c83c98,,cc5ed2f1,0b153874,a73ee510,3b08e48b,facf05cc,6d89b6a5,9f16a973,cfef1c29,1e744fde,2bd32e5c,776ce399,13145934,21ddcdc9,a458ea53,1419c3fc,,32c7478e,8ecc176a,e8b83407,a70a038a,"},
+ {"key": "example35", "data": "1,1,259,1,1,5,1,6,1,1,1,3,,1,05db9164,f3b07830,ad981000,f96c819d,25c83c98,,df5c2d18,0b153874,a73ee510,8aef4905,a7b606c4,b912be9f,eae197fd,b28479f6,d27eed0e,b8b09fe6,e5ba7672,048d01f4,,,08ae854d,,32c7478e,c657e6e5,,,"},
+ {"key": "example36", "data": "1,0,127,1,3,1683,19,26,17,475,0,9,0,3,05db9164,8947f767,11c9d79e,52a787c8,4cf72387,fbad5c96,18671b18,0b153874,a73ee510,ceb10289,77212bd7,79507c6b,7203f04e,07d13a8f,2c14c412,49013ffe,8efede7f,bd17c3da,f6a3e43b,a458ea53,35cd95c9,ad3062eb,c7dc6720,3fdb382b,010f6491,49d68486,"},
+ {"key": "example37", "data": "0,,1,,,23255,,0,1,73,,0,,,7e5c2ff4,d833535f,b00d1501,d16679b9,25c83c98,7e0ccccf,65c53f25,1f89b562,a73ee510,3b08e48b,ad2bc6f4,e0d76380,39ccb769,b28479f6,a733d362,1203a270,776ce399,281769c2,,,73d06dde,,32c7478e,aee52b6f,,,"},
+ {"key": "example38", "data": "0,6,-1,,,915,40,26,33,72,1,3,,,9a89b36c,4f25e98b,9042c4ea,343f8ed3,25c83c98,fbad5c96,27cc0b50,0b153874,a73ee510,f364a867,7671c62f,00750e7a,1fa0660e,b28479f6,df2f73e9,4f71659c,e5ba7672,bc5a0ff7,21ddcdc9,a458ea53,706ee322,c9d4222a,bcdee96c,990a118a,001f3601,47b6f269,"},
+ {"key": "example39", "data": "0,0,0,3,12,7308,97,2,21,90,0,1,,12,68fd1e64,9adf4cf9,723c059c,4c942c6d,4cf72387,7e0ccccf,ce4f7f55,0b153874,a73ee510,d7026747,38f692a7,ab60a748,6e5da64f,1adce6ef,808ff1bc,c23fc7ec,e5ba7672,2a93f7c8,,,5dc9a057,,32c7478e,90b6276f,,,"},
+ {"key": "example40", "data": "0,8,0,15,20,115,24,8,23,24,2,2,,20,5a9ed9b0,c66fca21,78171040,373c404a,25c83c98,,8ff6f5af,0b153874,a73ee510,5ba575e7,b5a9f90e,6766a7f0,949ea585,1adce6ef,8736735c,59974c9c,8efede7f,1304f63b,21ddcdc9,b1252a9d,07b2853e,,32c7478e,94bde4f2,010f6491,09b76f8d,"},
+ {"key": "example41", "data": "0,,38,2,4,3119,149,64,48,139,,6,6,4,05db9164,26a88120,d032c263,c18be181,4cf72387,fbad5c96,3f4ec687,1f89b562,a73ee510,726f00fd,c4adf918,dfbb09fb,85dbe138,07d13a8f,040ec437,84898b2a,8efede7f,57598e25,,,0014c32a,,32c7478e,3b183c5c,,,"},
+ {"key": "example42", "data": "1,88,319,,4,5,4,89,40,88,3,4,12,4,05db9164,08d6d899,333440d5,fc86bde0,25c83c98,fbad5c96,f00bddf8,0b153874,a73ee510,83ff688a,55795b33,1b0c8aa3,39795005,b28479f6,bffbd637,4a838997,8efede7f,bbf70d82,,,16e2e3b3,,32c7478e,d859b4dd,,,"},
+ {"key": "example43", "data": "0,,1,18,5,1683,80,38,5,95,,5,0,5,05db9164,09e68b86,aa8c1539,85dd697c,25c83c98,,2903ead3,0b153874,a73ee510,bcc8b4c6,a0a5e9d7,d8c29807,ee79db7b,1adce6ef,dcd06253,c64d548f,3486227d,63cdbb21,cf99e5de,a458ea53,5f957280,,32c7478e,1793a828,e8b83407,b7d9c3bc,"},
+ {"key": "example44", "data": "0,,27,,,112878,2106,0,2,95,,0,,,5a9ed9b0,38a947a1,2d8004c4,40ed41e5,25c83c98,7e0ccccf,4d9d55ae,5b392875,7cc72ec2,3b08e48b,55065437,ad972965,80dcea18,07d13a8f,c68ba31d,1206a8a1,d4bb7bd8,e96a7df2,,,54d8bb06,,3a171ecb,a415643d,,,"},
+ {"key": "example45", "data": "0,0,-1,,,4894,20,1,7,20,0,1,,,05db9164,4c2bc594,d032c263,c18be181,43b19349,7e0ccccf,7f52e00f,0b153874,a73ee510,48a4f593,bca79aeb,dfbb09fb,5218d824,8ceecbc8,7ac43a46,84898b2a,07c540c4,bc48b783,,,0014c32a,c9d4222a,3a171ecb,3b183c5c,,,"},
+ {"key": "example46", "data": "0,0,32,,1,9375,,0,37,18,0,0,0,1,05db9164,d833535f,ad4b77ff,d16679b9,25c83c98,7e0ccccf,9d547ce0,5b392875,a73ee510,3b08e48b,868a9e47,a2f4e8b5,fc5dea81,b28479f6,a733d362,89052618,3486227d,281769c2,,,d4703ebd,,32c7478e,aee52b6f,,,"},
+ {"key": "example47", "data": "0,,6,6,15,20213,507,7,42,360,,2,0,40,05db9164,0ca4b7d7,d032c263,c18be181,4cf72387,7e0ccccf,e9396c09,c8ddd494,a73ee510,3b08e48b,a0060bca,dfbb09fb,22d23aac,1adce6ef,9014f0f9,84898b2a,e5ba7672,c786d1ea,,,0014c32a,,32c7478e,3b183c5c,,,"},
+ {"key": "example48", "data": "0,31,17,2,11,290,23,31,23,65,2,2,,11,05db9164,4f25e98b,03280284,5214fda3,25c83c98,fbad5c96,0c41b6a1,0b153874,a73ee510,fa642b71,4ba74619,60bab41d,879fa878,07d13a8f,5be89da3,b6acbd10,e5ba7672,bc5a0ff7,fae651c5,a458ea53,3792328c,c0061c6d,423fab69,7a8e7ed6,001f3601,f159b6cb,"},
+ {"key": "example49", "data": "0,1,2382,13,4,40,4,69,3,609,1,11,0,4,05db9164,38a947a1,933cc823,b1c1e580,25c83c98,fe6b92e5,002fdf0c,1f89b562,a73ee510,61f70369,a4ea009a,2562cf3c,1e9339bc,b28479f6,f5bfabbd,03dee53f,e5ba7672,b3e92443,,,be661a75,,c7dc6720,67d37917,,,"},
+ {"key": "example50", "data": "0,0,190,,,1624,6,29,6,74,0,9,,,68fd1e64,c41a84c8,0a266224,759c4a2e,25c83c98,,804d2f11,0b153874,a73ee510,2860ede1,1aa6cf31,99dfd83a,3b03d76e,b28479f6,e3eb97c7,62b5674b,e5ba7672,5911fc7e,,,28ee216d,,32c7478e,590b856f,,,"},
+ {"key": "example51", "data": "0,0,19,8,9,1506,31,3,7,34,0,1,,9,05db9164,8947f767,100a3803,ad1b5124,30903e74,7e0ccccf,bb3b7ab9,c8ddd494,a73ee510,3b08e48b,90b202b5,d377c333,3a9dafb8,b28479f6,a473257f,68d2c2b9,8efede7f,bd17c3da,e51f040f,a458ea53,79c3f011,,bcdee96c,fe35ffe2,010f6491,987ea0be,"},
+ {"key": "example52", "data": "1,1,30,,3,116,31,1,3,3,1,1,,3,5a9ed9b0,38a947a1,0f90b6d6,16b922ed,25c83c98,fbad5c96,17b47bf9,0b153874,a73ee510,3b08e48b,76120d9d,ee2d3fdb,d4384424,07d13a8f,3046a70a,81371cbc,d4bb7bd8,1bae7658,,,37bad455,ad3062eb,3a171ecb,9d96bacb,,,"},
+ {"key": "example53", "data": "0,,6,2,3,2779,,0,3,13,,0,,3,fb174e6b,47e8ab98,b009d929,c7043c4b,384874ce,,646e7593,0b153874,a73ee510,3b08e48b,d05acfa9,3563ab62,969e14fd,1adce6ef,bfa6d08a,b688c8cc,8efede7f,eb4d3f8a,21ddcdc9,5840adea,2754aaf1,,55dd3565,3b183c5c,f55c04b6,491eeeef,"},
+ {"key": "example54", "data": "0,0,2,22,3,4687,242,6,6,183,0,1,4,3,05db9164,287130e0,c09cf4ef,bc8d1aa6,25c83c98,13718bbd,1919941b,37e4aa92,a73ee510,6c47047a,86c05043,c4bba41d,2ecea536,b28479f6,9efd8b77,ac2e5095,8efede7f,891589e7,2efde463,b1252a9d,dc4e98e3,,3a171ecb,ee42de86,e8b83407,a00829e6,"},
+ {"key": "example55", "data": "0,,55,16,7,1696,72,2,7,95,,2,,7,5bfa8ab5,89ddfee8,00e2b23c,10d65c35,25c83c98,7e0ccccf,ad3508b1,5b392875,a73ee510,fc3680e8,ad757a5a,f400e021,93b18cb5,1adce6ef,34cce7d2,9e87470c,e5ba7672,5bb2ec8e,7a45f7f2,a458ea53,a13d5eab,,423fab69,faf5d8b3,f0f449dd,a8cf207e,"},
+ {"key": "example56", "data": "0,6,0,28,0,31,0,6,0,0,1,1,,0,8cf07265,287130e0,c1ba4c5a,16fe249c,25c83c98,7e0ccccf,c1225605,985e3fcb,a73ee510,ede207dc,f29b9ed2,469027a9,7eaf6f1a,07d13a8f,10040656,8f13519e,e5ba7672,891589e7,6f3756eb,5840adea,f4095a39,,c7dc6720,1793a828,e8b83407,a475662f,"},
+ {"key": "example57", "data": "0,0,19,9,3,14414,1353,3,1,362,0,1,0,3,be589b51,09e68b86,4bee8a47,5031d726,25c83c98,7e0ccccf,197b4575,322e63df,a73ee510,6c47047a,48876b80,6f95f18b,e40e52ae,07d13a8f,36721ddc,3d66d729,8efede7f,5aed7436,21ddcdc9,a458ea53,3c1a8dd8,,3a171ecb,3fdb382b,b9266ff0,49d68486,"},
+ {"key": "example58", "data": "0,,18,12,8,8965,44,2,12,57,,2,0,8,05db9164,d7988e72,5eee7056,c1a3acf5,afcf7897,,96825c8f,0b153874,a73ee510,bc283a64,2df02cf1,f71904ea,03232503,b28479f6,c8389df7,0db58836,07c540c4,0f2f9850,5fd56cf9,b1252a9d,96725293,,32c7478e,2702453c,8b8de563,303cea07,"},
+ {"key": "example59", "data": "0,,1,2,0,177674,,0,3,2,,0,0,1,87552397,207b2d81,6e136288,4f938621,25c83c98,7e0ccccf,8025502e,6c41e35e,7cc72ec2,4072f40f,29e4ad33,64ddde07,80467802,07d13a8f,0bf0feff,0c41b634,e5ba7672,fa0643ee,21ddcdc9,b1252a9d,b4031b95,,3a171ecb,a81956df,001f3601,b1262ddd,"},
+ {"key": "example60", "data": "0,,0,20,0,4412,855,0,4,522,,0,,5,05db9164,58e67aaf,54e3c628,9725d851,25c83c98,7e0ccccf,5b18f3d9,0b153874,a73ee510,ad0b97fb,720446f5,92409ea2,034e5f3b,07d13a8f,10935a85,05c5cfbe,d4bb7bd8,c21c3e4c,6f62a118,b1252a9d,54d0b766,,c7dc6720,2913df0f,9b3e8820,bc7f21c2,"},
+ {"key": "example61", "data": "0,,56,2,,,,0,0,1,,0,,,5a9ed9b0,8084ee93,02cf9876,c18be181,25c83c98,fbad5c96,af0809a5,5b392875,7cc72ec2,3b08e48b,9e12e146,8fe001f4,025225f2,b28479f6,16d2748c,36103458,2005abd1,003d4f4f,,,e587c466,,be7c41b4,3b183c5c,,,"},
+ {"key": "example62", "data": "0,,76,5,,46200,,,7,,,,0,,68fd1e64,287130e0,7555338e,e161fae2,25c83c98,7e0ccccf,ce17d537,0b153874,7cc72ec2,ed111662,5b225578,f34e8f6a,d1be539d,07d13a8f,10040656,8ec308fc,3486227d,891589e7,21ddcdc9,5840adea,182fdd1a,,c7dc6720,6c1cdd05,ea9a246c,1219b447,"},
+ {"key": "example63", "data": "0,39,8,42,32,27,33,39,24,32,1,1,,32,05db9164,73a46ff0,844ce0a4,9bb11257,4cf72387,7e0ccccf,ff3f3dda,0fb392dd,a73ee510,3b08e48b,e2217f93,1d0b8187,da9ee8bd,1adce6ef,d57668e2,4372eb4b,e5ba7672,da507f45,21ddcdc9,5840adea,ecb5cd6f,,32c7478e,6dbd889f,ea9a246c,33ced911,"},
+ {"key": "example64", "data": "1,,0,55,5,14477,,0,5,1,,0,,5,05db9164,09e68b86,2beedeb2,c59396e7,25c83c98,7e0ccccf,a972360e,0b153874,7cc72ec2,acf0058d,9e511730,8f70e33a,04e4a7e0,64c94865,91126f30,6df0eed9,e5ba7672,5aed7436,55dd3565,5840adea,c412f773,,3a171ecb,3fdb382b,e8b83407,ccc71a58,"},
+ {"key": "example65", "data": "0,1,1,5,8,7,8,1,8,8,1,1,0,8,05db9164,8e4f887c,,,25c83c98,13718bbd,47802627,5b9f3341,a73ee510,fbbf2c95,42e01668,,79db54f6,07d13a8f,b708086d,,d4bb7bd8,4b340164,,,,,3a171ecb,,,,"},
+ {"key": "example66", "data": "1,,1,7,3,10087,67,6,3,57,,3,,3,05db9164,d7988e72,fb535e16,6fe3d332,25c83c98,13718bbd,2829f187,66f29b89,a73ee510,e034d733,3a9c7259,8e86918c,0d8d4492,07d13a8f,194c42a4,57236df8,e5ba7672,0f2f9850,c27239bd,5840adea,841f2712,,3a171ecb,1793a828,e8b83407,b820b6c5,"},
+ {"key": "example67", "data": "0,7,1,40,,1418,23,147,0,7,0,4,0,,68fd1e64,80e26c9b,ba1947d0,85dd697c,25c83c98,7e0ccccf,16401b7d,a61cc0ef,a73ee510,3b08e48b,20ec800a,34a238e0,18a5e4b8,b28479f6,a785131a,da441c7e,e5ba7672,005c6740,21ddcdc9,5840adea,8717ea07,,32c7478e,1793a828,e8b83407,b9809574,"},
+ {"key": "example68", "data": "0,,0,10,2,3545,,0,2,3,,0,,2,be589b51,e5fb1af3,50808b4e,39ec3719,25c83c98,13718bbd,316949b7,5b392875,a73ee510,3b08e48b,d51f40d7,75ef3efe,4eb5dabc,07d13a8f,b5de5956,d5cb04e4,776ce399,13145934,21ddcdc9,b1252a9d,1d4696ef,,32c7478e,39fe175c,e8b83407,1c7f8927,"},
+ {"key": "example69", "data": "0,5,51,,5,457,5,5,7,11,1,1,1,5,ae82ea21,9e5ce894,e1120103,13508380,25c83c98,7e0ccccf,6855ef53,0b153874,a73ee510,175d6c71,b7094596,d19a1cc6,1f9d2c38,07d13a8f,8cf98699,e58b9a62,3486227d,a5bb7b8a,1d1eb838,a458ea53,2eb5be02,ad3062eb,c7dc6720,45ab94c8,ea9a246c,c84c4aec,"},
+ {"key": "example70", "data": "0,0,0,15,2,20112,305,1,43,228,0,1,,30,05db9164,0a519c5c,02cf9876,c18be181,25c83c98,7e0ccccf,fe4e75fa,0b153874,a73ee510,6aea41c7,8f4f8f83,8fe001f4,8828a59c,07d13a8f,4ac81a35,36103458,d4bb7bd8,416e8695,,,e587c466,,93bad2c0,3b183c5c,,,"},
+ {"key": "example71", "data": "0,0,1,4,3,1689,184,12,46,53,0,1,,3,8cf07265,207b2d81,d6be853a,4842a03d,384874ce,fe6b92e5,209d1929,5b392875,a73ee510,9eff685e,87fe3e10,dadde5ca,3bd6c21d,b28479f6,3c767806,6077db2c,e5ba7672,395856b0,21ddcdc9,a458ea53,ae7b2d98,,32c7478e,b8942a02,001f3601,4a6648b5,"},
+ {"key": "example72", "data": "0,4,0,89,4,486,5,4,7,4,1,1,,4,05db9164,8947f767,22e8ec23,f8faa363,43b19349,7e0ccccf,45607029,51d76abe,a73ee510,0ada1061,2b9f131d,18e1f914,aca10c14,1adce6ef,ba8b8b16,226c87e7,d4bb7bd8,bd17c3da,656485cf,a458ea53,0443b252,,bcdee96c,c73755d6,e8b83407,c23979db,"},
+ {"key": "example73", "data": "1,,39,2,1,3343,1,17,2,1,,1,,1,05db9164,5a88f1d5,16424a73,08694bce,30903e74,fe6b92e5,12c61956,5b392875,a73ee510,52486df2,94d2aad8,c76cdf17,f23a3825,b28479f6,e842876b,ffe60785,e5ba7672,1adff463,,,5f0fcebd,ad3062eb,3a171ecb,392bf8f1,,,"},
+ {"key": "example74", "data": "0,,2,1,2,8036,164,18,13,367,,2,0,2,68fd1e64,287130e0,54597e12,12c911a7,384874ce,13718bbd,48b70cb6,985e3fcb,a73ee510,6123dced,1736789a,8c51bef7,1a347339,b28479f6,9efd8b77,f8f7edbf,3486227d,891589e7,99f90f6d,5840adea,72b1423f,,32c7478e,af062947,ea9a246c,b4a4615f,"},
+ {"key": "example75", "data": "0,,-1,,,71140,142,0,7,63,,0,,,8cf07265,4c2bc594,d032c263,c18be181,25c83c98,fbad5c96,5d859d57,0b153874,7cc72ec2,cd481139,00adbfbb,dfbb09fb,d4b85d8d,8ceecbc8,7ac43a46,84898b2a,07c540c4,bc48b783,,,0014c32a,,3a171ecb,3b183c5c,,,"},
+ {"key": "example76", "data": "1,,0,11,3,16184,125,2,3,103,,2,0,3,05db9164,f0cf0024,6f67f7e5,41274cd7,25c83c98,,94a113a4,0b153874,a73ee510,4ddb41b1,f47e21eb,623049e6,4f3f2bb1,b28479f6,e6c5b5cd,c92f3b61,07c540c4,b04e4670,21ddcdc9,5840adea,60f6221e,,32c7478e,43f13e8b,ea9a246c,731c3655,"},
+ {"key": "example77", "data": "0,,140,2,2,,,0,2,2,,0,,2,5bfa8ab5,38a947a1,,,25c83c98,7e0ccccf,88002ee1,64523cfa,7cc72ec2,3b08e48b,f1b78ab4,,6e5da64f,07d13a8f,c2b7aaa6,,2005abd1,659bdb63,,,,ad3062eb,32c7478e,,,,"},
+ {"key": "example78", "data": "0,2,0,,7,443,37,7,34,282,1,4,7,7,3c9d8785,38a947a1,4470baf4,8c8a4c47,43b19349,fbad5c96,282b88fc,0b153874,a73ee510,0f1a2599,ea26a3ee,bb669e25,0e5bc979,b28479f6,547b8c62,2b2ce127,8efede7f,b133fcd4,,,2b796e4a,,32c7478e,8d365d3b,,,"},
+ {"key": "example79", "data": "1,2,1,60,75,61,121,52,39,248,1,8,1,77,05db9164,942f9a8d,ab4a038c,fea9881c,4cf72387,7e0ccccf,3f4ec687,0b153874,a73ee510,726f00fd,c4adf918,2b7b1137,85dbe138,1adce6ef,ae97ecc3,2dbf1d23,8efede7f,1f868fdd,f44bef3c,a458ea53,8adfc28d,,bcdee96c,3fdb382b,9d93af03,49d68486,"},
+ {"key": "example80", "data": "0,1,34,2,42,328,44,15,49,58,1,9,0,42,05db9164,2c16a946,a65db9fb,9f43a1b5,25c83c98,,1d794a16,5b392875,a73ee510,ed086ca2,4c9e8313,28156fd4,67b031b4,b28479f6,3628a186,87140baa,e5ba7672,e4ca448c,,,67bb5322,,32c7478e,9117a34a,,,"},
+ {"key": "example81", "data": "0,,0,,,14919,,0,0,0,,0,,,05db9164,4c2bc594,d032c263,c18be181,384874ce,fe6b92e5,b7c924a4,64523cfa,a73ee510,3b08e48b,2cc0193e,dfbb09fb,433f9499,8ceecbc8,7ac43a46,84898b2a,1e88c74f,bc48b783,,,0014c32a,,3a171ecb,3b183c5c,,,"},
+ {"key": "example82", "data": "0,,1,,1,20216,66,1,1,1,,1,,1,68fd1e64,ae46a29d,c68767c4,f922efad,4cf72387,fbad5c96,5fd3419b,0b153874,a73ee510,972359d0,efc5e2cf,9e62f74b,c7176043,8ceecbc8,14d0a096,e2e2fcd9,d4bb7bd8,a573334c,,,3c35236f,,55dd3565,b34f3128,,,"},
+ {"key": "example83", "data": "0,13,13,80,32,378,115,15,37,57,1,2,,48,5a9ed9b0,4f25e98b,b393df87,c3fecae9,25c83c98,7e0ccccf,5192dba2,0b153874,a73ee510,f1317066,aaa08406,8d33fe00,6665daff,8ceecbc8,5525889d,0fd4fbad,e5ba7672,9e4517be,3014a4b1,5840adea,572bdde8,ad3062eb,32c7478e,9fa3e01a,001f3601,d9bcfc08,"},
+ {"key": "example84", "data": "0,,0,91,4,293,220,35,4,66,,5,1,4,05db9164,58e67aaf,1100da42,5024ec42,4cf72387,7e0ccccf,964d1fdd,37e4aa92,a73ee510,f007059b,59cd5ae7,a9d84aa8,8b216f7b,1adce6ef,d002b6d9,3b35c4c2,27c07bd6,c21c3e4c,21ddcdc9,a458ea53,e9ab8737,,423fab69,2c720b71,9b3e8820,47d6d9aa,"},
+ {"key": "example85", "data": "0,,-1,,,5654,,0,4,15,,0,0,,05db9164,09e68b86,d8d5b6da,bd97eb27,43b19349,fbad5c96,4b219154,1f89b562,a73ee510,7259dc52,f25fe7e9,a6cbd3ab,dd183b4c,07d13a8f,36721ddc,cd220c47,3486227d,5aed7436,338f20de,a458ea53,7e08e349,,32c7478e,661d665d,9d93af03,ee88160c,"},
+ {"key": "example86", "data": "0,0,53,,10,6550,98,34,11,349,0,9,,10,05db9164,207b2d81,8bd78c57,394ee067,25c83c98,6f6d9be8,283d5555,0b153874,a73ee510,3b08e48b,3d5fb018,e5f6b330,94172618,07d13a8f,0bf0feff,402a9036,e5ba7672,fa0643ee,21ddcdc9,b1252a9d,0094bc78,,32c7478e,29ece3ed,001f3601,402185f3,"},
+ {"key": "example87", "data": "1,6,-1,10,6,0,0,6,9,9,1,1,,0,05db9164,09e68b86,be7504db,2f91f54d,25c83c98,7e0ccccf,26a81064,5b392875,a73ee510,dcbc7c2b,9e511730,1205ef20,04e4a7e0,07d13a8f,36721ddc,9f064e1a,e5ba7672,5aed7436,1d04f4a4,a458ea53,c3444bea,,3a171ecb,3fdb382b,e8b83407,49d68486,"},
+ {"key": "example88", "data": "1,,0,1,3,2901,,0,3,9,,0,,3,05db9164,762b9a6f,b559a4bf,c7663617,25c83c98,,1f3230eb,1f89b562,a73ee510,687c8b31,05766aa3,051dfd94,b94e5df6,07d13a8f,aa600b94,dad720cc,1e88c74f,01890ebf,,,05afbcbe,,32c7478e,5a1a48d4,,,"},
+ {"key": "example89", "data": "0,1,23,1,14,56,15,10,35,161,1,2,,15,be589b51,38a947a1,,,25c83c98,,ba0ca6c5,0b153874,a73ee510,56ae5fb0,7ca01a9d,,97d749c9,07d13a8f,bbe9d023,,e5ba7672,f9c50fb1,,,,,32c7478e,,,,"},
+ {"key": "example90", "data": "0,,1,75,22,5912,181,2,22,68,,1,,22,5bfa8ab5,09e68b86,aa8c1539,85dd697c,4cf72387,,845fb196,5b392875,a73ee510,6997b535,911b463c,d8c29807,0c9cc756,8ceecbc8,d2f03b75,c64d548f,07c540c4,63cdbb21,cf99e5de,5840adea,5f957280,,32c7478e,1793a828,e8b83407,b7d9c3bc,"},
+ {"key": "example91", "data": "1,,127,2,23,15837,23,4,24,23,,1,,23,68fd1e64,d4be07ad,d8fd96ac,9581b80f,25c83c98,fe6b92e5,ea3f0578,0b153874,a73ee510,c514dac9,42880796,2af3c6b9,6e1e209e,07d13a8f,1936a526,94df6876,e5ba7672,cbae5931,1f4a1e60,5840adea,115892da,,c7dc6720,b2f178a3,001f3601,938732a0,"},
+ {"key": "example92", "data": "0,,-1,,,23204,,0,32,38,,0,,,68fd1e64,4c2bc594,d032c263,c18be181,25c83c98,fe6b92e5,3d63f4e6,0b153874,a73ee510,376bbe93,af6a4ffc,dfbb09fb,2a1579a2,1adce6ef,ae0c3875,84898b2a,8efede7f,15a36060,,,0014c32a,,423fab69,3b183c5c,,,"},
+ {"key": "example93", "data": "0,,-1,,,681386,,,11,,,,,,05db9164,4c2bc594,d032c263,c18be181,43b19349,7e0ccccf,1554a783,0b153874,7cc72ec2,7636f6c8,b7bb7a17,dfbb09fb,73e186f6,8ceecbc8,7ac43a46,84898b2a,e5ba7672,bc48b783,,,0014c32a,c9d4222a,3a171ecb,3b183c5c,,,"},
+ {"key": "example94", "data": "0,,1,64,11,1494,86,15,13,231,,4,,11,05db9164,8ab240be,f1fce64b,fbe29d7d,25c83c98,7e0ccccf,22eefdf2,0b153874,a73ee510,994730d3,974aaa98,51874c73,7889204d,07d13a8f,e7dd0bfc,d2f669a0,e5ba7672,ca533012,21ddcdc9,5840adea,30c1399f,,32c7478e,6856d4e9,b9266ff0,5f3ed85b,"},
+ {"key": "example95", "data": "0,,1,1,,2903,2,3,0,2,,1,,,05db9164,80e26c9b,aa8c1539,85dd697c,43b19349,fbad5c96,9c7e110d,0b153874,a73ee510,3b08e48b,3204e37d,d8c29807,8b4df3b4,1adce6ef,0f942372,c64d548f,07c540c4,005c6740,21ddcdc9,5840adea,5f957280,,3a171ecb,1793a828,e8b83407,9904c656,"},
+ {"key": "example96", "data": "0,,105,1,1,171519,,0,1,13,,0,,1,05db9164,38a947a1,145f2f75,82a61820,4cf72387,7e0ccccf,9d99aaa3,5b392875,7cc72ec2,2ce2764d,d93e6010,7161e106,4e8bba73,1adce6ef,402ce7c7,bb6d240e,07c540c4,e96a7df2,,,5fe17899,,423fab69,cafb4e4d,,,"},
+ {"key": "example97", "data": "0,,35,90,2,41230,352,0,5,44,,0,,5,05db9164,c5fe64d9,4e14c1ea,cb4db510,25c83c98,7e0ccccf,77e91f62,1f89b562,a73ee510,1a88cf9b,7defe259,4ec1453d,11fa2c12,b28479f6,543c0413,9cdd6d81,e5ba7672,c235abed,4632bcdc,b1252a9d,4bab48f8,,c7dc6720,3fdb382b,ea9a246c,49d68486,"},
+ {"key": "example98", "data": "0,,0,78,1,15835,220,1,1,1,,1,,1,05db9164,09e68b86,adeee51f,c9add53e,25c83c98,fe6b92e5,a255dd63,0b153874,a73ee510,4effc25c,d13e1160,75762013,45820f61,07d13a8f,36721ddc,afb79297,d4bb7bd8,5aed7436,363a4009,b1252a9d,aaf7d15f,,3a171ecb,1793a828,e8b83407,d94377ca,"},
+ {"key": "example99", "data": "0,,1715,,1,7545,19,15,1,87,,7,0,1,5a9ed9b0,999aae35,79bc99b4,e5e453f3,25c83c98,7e0ccccf,e13ff1c4,1f89b562,a73ee510,fbbf2c95,15cd287d,424e28fe,86c79eb0,243a4e68,39a6addf,a6a69939,3486227d,63aa00dd,,,424af181,,3a171ecb,869caea3,,,"},
+ {"key": "example100", "data": "0,0,48,3,2,14482,377,8,2,283,0,2,,2,05db9164,09e68b86,b3bebfb8,bc93ba48,43b19349,,46319bad,0b153874,a73ee510,14e49183,c92f4124,49fe61e3,c576c612,b28479f6,52baadf5,dc12c38d,e5ba7672,5aed7436,21ddcdc9,b1252a9d,0421c25a,,32c7478e,a212d987,e8b83407,f82917a3"}
]
}
diff --git a/tests/endtoendtests/OdpSampleNetwork/src/com/example/odpsamplenetwork/SampleHandler.java b/tests/endtoendtests/OdpSampleNetwork/src/com/example/odpsamplenetwork/SampleHandler.java
index d0e255e5..1bc4306e 100644
--- a/tests/endtoendtests/OdpSampleNetwork/src/com/example/odpsamplenetwork/SampleHandler.java
+++ b/tests/endtoendtests/OdpSampleNetwork/src/com/example/odpsamplenetwork/SampleHandler.java
@@ -34,8 +34,8 @@ import android.adservices.ondevicepersonalization.RenderInput;
import android.adservices.ondevicepersonalization.RenderOutput;
import android.adservices.ondevicepersonalization.RenderingConfig;
import android.adservices.ondevicepersonalization.RequestLogRecord;
-import android.adservices.ondevicepersonalization.TrainingExampleInput;
-import android.adservices.ondevicepersonalization.TrainingExampleOutput;
+import android.adservices.ondevicepersonalization.TrainingExamplesInput;
+import android.adservices.ondevicepersonalization.TrainingExamplesOutput;
import android.adservices.ondevicepersonalization.TrainingInterval;
import android.adservices.ondevicepersonalization.UserData;
import android.content.ContentValues;
@@ -70,10 +70,12 @@ import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.function.Consumer;
@@ -145,42 +147,37 @@ public class SampleHandler implements IsolatedWorker {
consumer.accept(downloadResult);
}
- @Override public void onExecute(
- @NonNull ExecuteInput input,
- @NonNull Consumer<ExecuteOutput> consumer
- ) {
+ @Override
+ public void onExecute(@NonNull ExecuteInput input, @NonNull Consumer<ExecuteOutput> consumer) {
Log.d(TAG, "onExecute() started.");
sBackgroundExecutor.execute(() -> handleOnExecute(input, consumer));
}
- @Override public void onTrainingExample(
- @NonNull TrainingExampleInput input,
- @NonNull Consumer<TrainingExampleOutput> consumer) {
- Log.d(TAG, "onTrainingExample() started.");
- sBackgroundExecutor.execute(() -> handleOnTrainingExample(input, consumer));
+ @Override
+ public void onTrainingExamples(
+ @NonNull TrainingExamplesInput input,
+ @NonNull Consumer<TrainingExamplesOutput> consumer) {
+ Log.d(TAG, "onTrainingExamples() started.");
+ sBackgroundExecutor.execute(() -> handleOnTrainingExamples(input, consumer));
}
- @Override public void onRender(
- @NonNull RenderInput input,
- @NonNull Consumer<RenderOutput> consumer
- ) {
+ @Override
+ public void onRender(@NonNull RenderInput input, @NonNull Consumer<RenderOutput> consumer) {
Log.d(TAG, "onRender() started.");
sBackgroundExecutor.execute(() -> handleOnRender(input, consumer));
}
- @Override public void onEvent(
- @NonNull EventInput input,
- @NonNull Consumer<EventOutput> consumer) {
+ @Override
+ public void onEvent(@NonNull EventInput input, @NonNull Consumer<EventOutput> consumer) {
Log.d(TAG, "onEvent() started.");
- sBackgroundExecutor.execute(
- () -> handleOnWebViewEvent(input, consumer));
+ sBackgroundExecutor.execute(() -> handleOnWebViewEvent(input, consumer));
}
private ListenableFuture<List<Ad>> readAds(KeyValueStore remoteData) {
Log.d(TAG, "readAds() called.");
try {
ArrayList<Ad> ads = new ArrayList<>();
- for (var key: remoteData.keySet()) {
+ for (var key : remoteData.keySet()) {
if (!key.startsWith("ad")) {
continue;
}
@@ -240,7 +237,7 @@ public class SampleHandler implements IsolatedWorker {
String requestKeyword = "";
if (input != null && input.getAppParams() != null
&& input.getAppParams().getString("keyword") != null) {
- requestKeyword = input.getAppParams().getString("keyword");
+ requestKeyword = input.getAppParams().getString("keyword").toLowerCase().strip();
}
List<Ad> result = new ArrayList<>();
@@ -282,73 +279,55 @@ public class SampleHandler implements IsolatedWorker {
.build();
}
- static Feature convertStringToFeature(String value) {
+ private static Feature convertStringToFeature(String value) {
BytesList.Builder bytesListBuilder = BytesList.newBuilder();
String nonNullValue = Strings.nullToEmpty(value);
bytesListBuilder.addValue(ByteString.copyFromUtf8(nonNullValue));
return Feature.newBuilder().setBytesList(bytesListBuilder.build()).build();
}
- static Feature convertLongToFeature(long value) {
+ private static Feature convertLongToFeature(String value) {
+ long lValue = value.isEmpty() ? 0L : Long.parseLong(value);
return Feature.newBuilder()
- .setInt64List(Int64List.newBuilder().addValue(value).build())
+ .setInt64List(Int64List.newBuilder().addValue(lValue).build())
.build();
}
- private void handleOnTrainingExample(
- @NonNull TrainingExampleInput input,
- @NonNull Consumer<TrainingExampleOutput> consumer) {
+ private static Example convertToExample(String serializedExample) {
+ String[] splitExample = serializedExample.split(",", -1);
Features.Builder featuresBuilder = Features.newBuilder();
+ featuresBuilder.putFeature("clicked", convertLongToFeature(splitExample[0]));
+
+ int count = 1;
+ for (; count < 14; count++) {
+ featuresBuilder.putFeature(
+ String.format("int-feature-%d", count),
+ convertLongToFeature(splitExample[count]));
+ }
+ for (; count < 40; count++) {
+ featuresBuilder.putFeature(
+ String.format("categorical-feature-%d", count),
+ convertStringToFeature(splitExample[count]));
+ }
+ return Example.newBuilder().setFeatures(featuresBuilder.build()).build();
+ }
- featuresBuilder.putFeature("int-feature-1", convertLongToFeature(0L));
- featuresBuilder.putFeature("int-feature-2", convertLongToFeature(0L));
- featuresBuilder.putFeature("int-feature-3", convertLongToFeature(0L));
- featuresBuilder.putFeature("int-feature-4", convertLongToFeature(0L));
- featuresBuilder.putFeature("int-feature-5", convertLongToFeature(0L));
- featuresBuilder.putFeature("int-feature-6", convertLongToFeature(0L));
- featuresBuilder.putFeature("int-feature-7", convertLongToFeature(0L));
- featuresBuilder.putFeature("int-feature-8", convertLongToFeature(0L));
- featuresBuilder.putFeature("int-feature-9", convertLongToFeature(0L));
- featuresBuilder.putFeature("int-feature-10", convertLongToFeature(0L));
- featuresBuilder.putFeature("int-feature-11", convertLongToFeature(0L));
- featuresBuilder.putFeature("int-feature-12", convertLongToFeature(0L));
- featuresBuilder.putFeature("int-feature-13", convertLongToFeature(0L));
-
- featuresBuilder.putFeature("categorical-feature-14", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-15", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-16", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-17", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-18", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-19", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-20", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-21", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-22", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-23", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-24", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-25", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-26", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-27", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-28", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-29", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-30", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-31", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-32", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-33", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-34", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-35", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-36", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-37", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-38", convertStringToFeature(""));
- featuresBuilder.putFeature("categorical-feature-39", convertStringToFeature(""));
-
- featuresBuilder.putFeature("clicked", convertLongToFeature(1L));
-
- Example example = Example.newBuilder().setFeatures(featuresBuilder.build()).build();
- TrainingExampleOutput result = new TrainingExampleOutput
- .Builder()
- .addTrainingExample(example.toByteArray())
- .addResumptionToken("token1".getBytes()).build();
- consumer.accept(result);
+ private void handleOnTrainingExamples(
+ @NonNull TrainingExamplesInput input,
+ @NonNull Consumer<TrainingExamplesOutput> consumer) {
+ TrainingExamplesOutput.Builder resultBuilder = new TrainingExamplesOutput.Builder();
+ Random rand = new Random();
+ int numExample = rand.nextInt(10) + 1;
+ Log.d(TAG, String.format("onTrainingExample() generates %d examples.", numExample));
+ for (int count = 0; count < numExample; count++) {
+ Example example = convertToExample(
+ new String(mRemoteData.get(String.format("example%d", rand.nextInt(100) + 1)),
+ StandardCharsets.UTF_8));
+ resultBuilder.addTrainingExample(
+ example.toByteArray()).addResumptionToken(
+ String.format("token%d", count).getBytes());
+ }
+ consumer.accept(resultBuilder.build());
}
private void handleOnExecute(
@@ -469,7 +448,8 @@ public class SampleHandler implements IsolatedWorker {
}
long now = System.currentTimeMillis();
List<EventLogRecord> logRecords = mLogReader.getJoinedEvents(
- now - 24 * 60 * 60 * 1000, now);
+ Instant.ofEpochMilli(now - 24 * 60 * 60 * 1000),
+ Instant.ofEpochMilli(now));
EventLogRecord found = null;
// Attribute conversion to most recent impression or click.
for (EventLogRecord ev : logRecords) {
@@ -646,7 +626,7 @@ public class SampleHandler implements IsolatedWorker {
if (ad != null && !isBlockedAd(ad)) {
filteredKeys.add(key);
}
- } else if (key.startsWith("template")) {
+ } else if (key.startsWith("template") || key.startsWith("example")) {
filteredKeys.add(key);
}
}
diff --git a/tests/federatedcomputetests/AndroidManifest.xml b/tests/federatedcomputetests/AndroidManifest.xml
index 9767a489..09591632 100644
--- a/tests/federatedcomputetests/AndroidManifest.xml
+++ b/tests/federatedcomputetests/AndroidManifest.xml
@@ -32,6 +32,10 @@
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
+ <service android:name="com.android.federatedcompute.services.encryption.BackgroundKeyFetchJobService"
+ android:exported="false"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
<service android:name="com.android.federatedcompute.services.training.IsolatedTrainingService"
android:isolatedProcess="true" android:exported="false" >
</service>
diff --git a/tests/federatedcomputetests/src/com/android/federatedcompute/services/FederatedComputeManagingServiceImplTest.java b/tests/federatedcomputetests/src/com/android/federatedcompute/services/FederatedComputeManagingServiceImplTest.java
index 43e98e72..ecdeddab 100644
--- a/tests/federatedcomputetests/src/com/android/federatedcompute/services/FederatedComputeManagingServiceImplTest.java
+++ b/tests/federatedcomputetests/src/com/android/federatedcompute/services/FederatedComputeManagingServiceImplTest.java
@@ -17,7 +17,9 @@
package com.android.federatedcompute.services;
import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import android.content.Intent;
import android.os.IBinder;
@@ -25,6 +27,7 @@ import android.os.IBinder;
import androidx.test.core.app.ApplicationProvider;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.federatedcompute.services.encryption.BackgroundKeyFetchJobService;
import org.junit.Before;
import org.junit.Test;
@@ -39,7 +42,10 @@ public final class FederatedComputeManagingServiceImplTest {
@Test
public void testBindableFederatedComputeService() {
- MockitoSession session = ExtendedMockito.mockitoSession().startMocking();
+ MockitoSession session = ExtendedMockito.mockitoSession()
+ .spyStatic(BackgroundKeyFetchJobService.class).startMocking();
+ ExtendedMockito.doReturn(true)
+ .when(() -> BackgroundKeyFetchJobService.scheduleJobIfNeeded(any(), any()));
try {
FederatedComputeManagingServiceImpl spyFcpService =
spy(new FederatedComputeManagingServiceImpl());
@@ -49,6 +55,8 @@ public final class FederatedComputeManagingServiceImplTest {
ApplicationProvider.getApplicationContext(),
FederatedComputeManagingServiceImpl.class);
IBinder binder = spyFcpService.onBind(intent);
+ ExtendedMockito.verify(() -> BackgroundKeyFetchJobService.scheduleJobIfNeeded(
+ any(), any()), times(1));
assertNotNull(binder);
} finally {
session.finishMocking();
diff --git a/tests/federatedcomputetests/src/com/android/federatedcompute/services/common/PhFlagsTest.java b/tests/federatedcomputetests/src/com/android/federatedcompute/services/common/PhFlagsTest.java
index 10c362c3..90340bf9 100644
--- a/tests/federatedcomputetests/src/com/android/federatedcompute/services/common/PhFlagsTest.java
+++ b/tests/federatedcomputetests/src/com/android/federatedcompute/services/common/PhFlagsTest.java
@@ -17,20 +17,20 @@
package com.android.federatedcompute.services.common;
import static com.android.federatedcompute.services.common.Flags.FEDERATED_COMPUTE_GLOBAL_KILL_SWITCH;
+import static com.android.federatedcompute.services.common.PhFlags.FEDERATED_COMPUTATION_ENCRYPTION_KEY_DOWNLOAD_URL;
import static com.android.federatedcompute.services.common.PhFlags.KEY_FEDERATED_COMPUTE_KILL_SWITCH;
import static com.google.common.truth.Truth.assertThat;
import android.provider.DeviceConfig;
-import androidx.test.runner.AndroidJUnit4;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
/** Unit tests for {@link com.android.ondevicepersonalization.service.PhFlags} */
-@RunWith(AndroidJUnit4.class)
+@RunWith(JUnit4.class)
public class PhFlagsTest {
/** Get necessary permissions to access Setting.Config API and set up context */
@Before
@@ -60,4 +60,18 @@ public class PhFlagsTest {
Flags phFlags = FlagsFactory.getFlags();
assertThat(phFlags.getGlobalKillSwitch()).isEqualTo(phOverridingValue);
}
+
+ @Test
+ public void testGetEncryptionKeyFetchUrl() {
+ // Now overriding with the value from PH.
+ String overrideUrl = "https://real-coordinator/v1alpha/publicKeys";
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+ FEDERATED_COMPUTATION_ENCRYPTION_KEY_DOWNLOAD_URL,
+ overrideUrl,
+ /* makeDefault= */ false);
+
+ Flags phFlags = FlagsFactory.getFlags();
+ assertThat(phFlags.getEncryptionKeyFetchUrl()).isEqualTo(overrideUrl);
+ }
}
diff --git a/tests/federatedcomputetests/src/com/android/federatedcompute/services/data/FederatedTrainingTaskDaoTest.java b/tests/federatedcomputetests/src/com/android/federatedcompute/services/data/FederatedTrainingTaskDaoTest.java
index 6daff32d..b6f1bbc2 100644
--- a/tests/federatedcomputetests/src/com/android/federatedcompute/services/data/FederatedTrainingTaskDaoTest.java
+++ b/tests/federatedcomputetests/src/com/android/federatedcompute/services/data/FederatedTrainingTaskDaoTest.java
@@ -59,9 +59,8 @@ public final class FederatedTrainingTaskDaoTest {
}
@After
- public void cleanUp() throws Exception {
- FederatedComputeDbHelper dbHelper =
- FederatedComputeDbHelper.getInstanceForTest(mContext);
+ public void cleanUp() {
+ FederatedComputeDbHelper dbHelper = FederatedComputeDbHelper.getInstanceForTest(mContext);
dbHelper.getWritableDatabase().close();
dbHelper.getReadableDatabase().close();
dbHelper.close();
@@ -96,6 +95,7 @@ public final class FederatedTrainingTaskDaoTest {
mTrainingTaskDao.updateOrInsertFederatedTrainingTask(task);
FederatedTrainingTask task2 =
createDefaultFederatedTrainingTask().toBuilder()
+ .jobId(456)
.populationName(POPULATION_NAME + "_2")
.build();
mTrainingTaskDao.updateOrInsertFederatedTrainingTask(task2);
@@ -122,6 +122,7 @@ public final class FederatedTrainingTaskDaoTest {
mTrainingTaskDao.updateOrInsertFederatedTrainingTask(task);
FederatedTrainingTask task2 =
createDefaultFederatedTrainingTask().toBuilder()
+ .jobId(456)
.populationName(POPULATION_NAME + "_2")
.build();
mTrainingTaskDao.updateOrInsertFederatedTrainingTask(task2);
diff --git a/tests/federatedcomputetests/src/com/android/federatedcompute/services/encryption/BackgroundKeyFetchJobServiceTest.java b/tests/federatedcomputetests/src/com/android/federatedcompute/services/encryption/BackgroundKeyFetchJobServiceTest.java
new file mode 100644
index 00000000..0d1e410c
--- /dev/null
+++ b/tests/federatedcomputetests/src/com/android/federatedcompute/services/encryption/BackgroundKeyFetchJobServiceTest.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.federatedcompute.services.encryption;
+
+import static com.android.federatedcompute.services.data.FederatedComputeEncryptionKey.KEY_TYPE_ENCRYPTION;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.federatedcompute.services.common.FederatedComputeExecutors;
+import com.android.federatedcompute.services.common.FederatedComputeJobInfo;
+import com.android.federatedcompute.services.common.FlagsFactory;
+import com.android.federatedcompute.services.common.MonotonicClock;
+import com.android.federatedcompute.services.common.PhFlagsTestUtil;
+import com.android.federatedcompute.services.data.FederatedComputeDbHelper;
+import com.android.federatedcompute.services.data.FederatedComputeEncryptionKey;
+import com.android.federatedcompute.services.data.FederatedComputeEncryptionKeyDao;
+import com.android.federatedcompute.services.http.HttpClient;
+
+import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+// TODO: add tests with Ph flags
+@RunWith(JUnit4.class)
+public class BackgroundKeyFetchJobServiceTest {
+
+ private BackgroundKeyFetchJobService mSpyService;
+
+ private MockitoSession mStaticMockSession;
+
+ private Context mContext;
+
+ private HttpClient mHttpClient;
+
+ public FederatedComputeEncryptionKeyDao mEncryptionDao;
+
+ public FederatedComputeEncryptionKeyManager mSpyKeyManager;
+
+ private TestInjector mInjector;
+
+ @Before
+ public void setUp() throws Exception {
+ PhFlagsTestUtil.setUpDeviceConfigPermissions();
+ PhFlagsTestUtil.disableGlobalKillSwitch();
+ MockitoAnnotations.initMocks(this);
+ mContext = ApplicationProvider.getApplicationContext();
+ mInjector = new TestInjector();
+ mEncryptionDao = FederatedComputeEncryptionKeyDao.getInstanceForTest(mContext);
+ mHttpClient = new HttpClient();
+ mSpyService = spy(new BackgroundKeyFetchJobService(new TestInjector()));
+ doReturn(mSpyService).when(mSpyService).getApplicationContext();
+ doNothing().when(mSpyService).jobFinished(any(), anyBoolean());
+ JobScheduler jobScheduler = mContext.getSystemService(JobScheduler.class);
+ jobScheduler.cancel(FederatedComputeJobInfo.ENCRYPTION_KEY_FETCH_JOB_ID);
+ mSpyKeyManager =
+ spy(
+ new FederatedComputeEncryptionKeyManager(
+ MonotonicClock.getInstance(),
+ mEncryptionDao,
+ FlagsFactory.getFlags(),
+ mHttpClient,
+ MoreExecutors.newDirectExecutorService()));
+ mStaticMockSession =
+ ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+ }
+
+ @After
+ public void tearDown() {
+ if (mStaticMockSession != null) {
+ mStaticMockSession.finishMocking();
+ }
+
+ FederatedComputeDbHelper dbHelper = FederatedComputeDbHelper.getInstanceForTest(mContext);
+ dbHelper.getWritableDatabase().close();
+ dbHelper.getReadableDatabase().close();
+ dbHelper.close();
+ }
+
+ @Test
+ public void testOnStartJob() {
+ FederatedComputeEncryptionKeyManager keyManager =
+ mInjector.getEncryptionKeyManager(mContext);
+ List<FederatedComputeEncryptionKey> emptyKeyList = List.of();
+ doReturn(FluentFuture.from(Futures.immediateFuture(emptyKeyList)))
+ .when(keyManager)
+ .fetchAndPersistActiveKeys(KEY_TYPE_ENCRYPTION, /* isScheduledJob= */ true);
+
+ mSpyService.run(mock(JobParameters.class));
+
+ verify(mSpyService, times(1)).onStartJob(any());
+ verify(mSpyService, times(1)).jobFinished(any(), anyBoolean());
+ }
+
+ @Test
+ public void testOnStartJob_onFailure() {
+ FederatedComputeEncryptionKeyManager keyManager =
+ mInjector.getEncryptionKeyManager(mContext);
+ doReturn(
+ FluentFuture.from(
+ Futures.immediateFailedFuture(
+ new ExecutionException(
+ " Failed to fetch keys",
+ new IllegalStateException("http 404")))))
+ .when(keyManager)
+ .fetchAndPersistActiveKeys(KEY_TYPE_ENCRYPTION, /* isScheduledJob= */ true);
+
+ mSpyService.run(mock(JobParameters.class));
+
+ verify(mSpyService, times(1)).onStartJob(any());
+ verify(mSpyService, times(1)).jobFinished(any(), anyBoolean());
+ }
+
+ @Test
+ public void testScheduleJob() {
+ final JobScheduler jobScheduler = mContext.getSystemService(JobScheduler.class);
+
+ assertThat(
+ BackgroundKeyFetchJobService.scheduleJobIfNeeded(
+ mContext, FlagsFactory.getFlags()))
+ .isEqualTo(true);
+
+ final JobInfo scheduledJob =
+ jobScheduler.getPendingJob(
+ FederatedComputeJobInfo.ENCRYPTION_KEY_FETCH_JOB_ID);
+
+ assertThat(scheduledJob.getId())
+ .isEqualTo(FederatedComputeJobInfo.ENCRYPTION_KEY_FETCH_JOB_ID);
+ }
+
+ @Test
+ public void testScheduleJob_notNeeded() {
+ assertThat(
+ BackgroundKeyFetchJobService.scheduleJobIfNeeded(
+ mContext, FlagsFactory.getFlags()))
+ .isEqualTo(true);
+
+ assertThat(
+ BackgroundKeyFetchJobService.scheduleJobIfNeeded(
+ mContext, FlagsFactory.getFlags()))
+ .isEqualTo(false);
+ }
+
+ @Test
+ public void testOnStopJob() {
+ assertFalse(mSpyService.onStopJob(mock(JobParameters.class)));
+ }
+
+ @Test
+ public void testOnStartJob_enableKillSwitch() {
+ PhFlagsTestUtil.enableGlobalKillSwitch();
+ FederatedComputeEncryptionKeyManager keyManager =
+ mInjector.getEncryptionKeyManager(mContext);
+ List<FederatedComputeEncryptionKey> emptyKeyList = List.of();
+ doReturn(FluentFuture.from(Futures.immediateFuture(emptyKeyList)))
+ .when(keyManager)
+ .fetchAndPersistActiveKeys(KEY_TYPE_ENCRYPTION, /* isScheduledJob= */ true);
+
+ mSpyService.run(mock(JobParameters.class));
+
+ verify(mSpyService, times(1)).onStartJob(any());
+ verify(mSpyService, times(1)).jobFinished(any(), anyBoolean());
+ verify(keyManager, never()).fetchAndPersistActiveKeys(KEY_TYPE_ENCRYPTION,
+ /* isScheduledJob= */ true);
+ }
+
+ @Test
+ public void testDefaultInjector() {
+ BackgroundKeyFetchJobService.Injector injector =
+ new BackgroundKeyFetchJobService.Injector();
+ assertThat(injector.getExecutor())
+ .isEqualTo(FederatedComputeExecutors.getBackgroundExecutor());
+ assertThat(injector.getEncryptionKeyManager(mContext))
+ .isEqualTo(FederatedComputeEncryptionKeyManager.getInstance(mContext));
+ assertThat(injector.getLightWeightExecutor())
+ .isEqualTo(FederatedComputeExecutors.getLightweightExecutor());
+ }
+
+ class TestInjector extends BackgroundKeyFetchJobService.Injector {
+ @Override
+ ListeningExecutorService getExecutor() {
+ return MoreExecutors.newDirectExecutorService();
+ }
+
+ @Override
+ ListeningExecutorService getLightWeightExecutor() {
+ return MoreExecutors.newDirectExecutorService();
+ }
+
+ @Override
+ FederatedComputeEncryptionKeyManager getEncryptionKeyManager(Context context) {
+ return mSpyKeyManager;
+ }
+ }
+}
diff --git a/tests/federatedcomputetests/src/com/android/federatedcompute/services/encryption/FederatedComputeKeyFetchManagerTest.java b/tests/federatedcomputetests/src/com/android/federatedcompute/services/encryption/FederatedComputeKeyFetchManagerTest.java
new file mode 100644
index 00000000..5a1471e1
--- /dev/null
+++ b/tests/federatedcomputetests/src/com/android/federatedcompute/services/encryption/FederatedComputeKeyFetchManagerTest.java
@@ -0,0 +1,452 @@
+/*
+ * 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 com.android.federatedcompute.services.encryption;
+
+import static com.android.federatedcompute.services.data.FederatedComputeEncryptionKey.KEY_TYPE_ENCRYPTION;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.federatedcompute.services.common.Clock;
+import com.android.federatedcompute.services.common.FederatedComputeExecutors;
+import com.android.federatedcompute.services.common.Flags;
+import com.android.federatedcompute.services.common.MonotonicClock;
+import com.android.federatedcompute.services.data.FederatedComputeDbHelper;
+import com.android.federatedcompute.services.data.FederatedComputeEncryptionKey;
+import com.android.federatedcompute.services.data.FederatedComputeEncryptionKeyDao;
+import com.android.federatedcompute.services.http.FederatedComputeHttpResponse;
+import com.android.federatedcompute.services.http.HttpClient;
+
+import com.google.common.util.concurrent.Futures;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+
+@RunWith(AndroidJUnit4.class)
+public class FederatedComputeKeyFetchManagerTest {
+
+ private static final Map<String, List<String>> SAMPLE_RESPONSE_HEADER =
+ Map.of(
+ "Cache-Control", List.of("public,max-age=6000"),
+ "Age", List.of("1"),
+ "Content-Type", List.of("json"));
+
+ private static final String SAMPLE_RESPONSE_PAYLOAD =
+ """
+{ "keys": [{ "id": "0cc9b4c9-08bd", "key": "BQo+c1Tw6TaQ+VH/b+9PegZOjHuKAFkl8QdmS0IjRj8" """
+ + "} ] }";
+
+ private FederatedComputeEncryptionKeyManager mFederatedComputeEncryptionKeyManager;
+
+ @Mock private HttpClient mMockHttpClient;
+
+ private FederatedComputeEncryptionKeyDao mEncryptionKeyDao;
+
+ private Context mContext;
+
+ private Clock mClock;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = ApplicationProvider.getApplicationContext();
+ mClock = MonotonicClock.getInstance();
+ mEncryptionKeyDao = FederatedComputeEncryptionKeyDao.getInstanceForTest(mContext);
+ Flags mockFlags = Mockito.mock(Flags.class);
+ mFederatedComputeEncryptionKeyManager =
+ new FederatedComputeEncryptionKeyManager(
+ mClock,
+ mEncryptionKeyDao,
+ mockFlags,
+ mMockHttpClient,
+ FederatedComputeExecutors.getBackgroundExecutor());
+ String overrideUrl = "https://real-coordinator/v1alpha/publicKeys";
+ doReturn(overrideUrl).when(mockFlags).getEncryptionKeyFetchUrl();
+ }
+
+ @After
+ public void teadDown() {
+ FederatedComputeDbHelper dbHelper = FederatedComputeDbHelper.getInstanceForTest(mContext);
+ dbHelper.getWritableDatabase().close();
+ dbHelper.getReadableDatabase().close();
+ dbHelper.close();
+ }
+
+ @Test
+ public void testGetTTL_fullInfo() {
+ Map<String, List<String>> headers = new HashMap<>();
+ headers.put("Cache-Control", List.of("public,max-age=3600"));
+ headers.put("Age", List.of("8"));
+
+ long ttl = FederatedComputeEncryptionKeyManager.getTTL(headers);
+
+ assertThat(ttl).isEqualTo(3600 - 8);
+ }
+
+ @Test
+ public void testGetTTL_noCache() {
+ Map<String, List<String>> headers = new HashMap<>();
+ headers.put("Age", List.of("8"));
+
+ long ttl = FederatedComputeEncryptionKeyManager.getTTL(headers);
+
+ assertThat(ttl).isEqualTo(0);
+ }
+
+ @Test
+ public void testGetTTL_noAge() {
+ Map<String, List<String>> headers = new HashMap<>();
+ headers.put("Cache-Control", List.of("public,max-age=3600"));
+
+ long ttl = FederatedComputeEncryptionKeyManager.getTTL(headers);
+
+ assertThat(ttl).isEqualTo(3600);
+ }
+
+ @Test
+ public void testGetTTL_empty() {
+ Map<String, List<String>> headers = Collections.EMPTY_MAP;
+
+ long ttl = FederatedComputeEncryptionKeyManager.getTTL(headers);
+
+ assertThat(ttl).isEqualTo(0);
+ }
+
+ @Test
+ public void testGetTTL_failedParse() {
+ Map<String, List<String>> headers = new HashMap<>();
+ headers.put("Cache-Control", List.of("public,max-age==3600"));
+ headers.put("Age", List.of("8"));
+
+ long ttl = FederatedComputeEncryptionKeyManager.getTTL(headers);
+
+ assertThat(ttl).isEqualTo(0);
+ }
+
+ @Test
+ public void testFetchAndPersistActiveKeys_scheduled_success() throws Exception {
+ doReturn(
+ Futures.immediateFuture(
+ new FederatedComputeHttpResponse.Builder()
+ .setHeaders(SAMPLE_RESPONSE_HEADER)
+ .setPayload(SAMPLE_RESPONSE_PAYLOAD.getBytes())
+ .setStatusCode(200)
+ .build()))
+ .when(mMockHttpClient)
+ .performRequestAsyncWithRetry(any());
+
+ List<FederatedComputeEncryptionKey> keys =
+ mFederatedComputeEncryptionKeyManager
+ .fetchAndPersistActiveKeys(KEY_TYPE_ENCRYPTION, /* isScheduledJob= */ true)
+ .get();
+
+ assertThat(keys.size()).isGreaterThan(0);
+ }
+
+ @Test
+ public void testFetchAndPersistActiveKeys_nonScheduled_success() throws Exception {
+ doReturn(
+ Futures.immediateFuture(
+ new FederatedComputeHttpResponse.Builder()
+ .setHeaders(SAMPLE_RESPONSE_HEADER)
+ .setPayload(SAMPLE_RESPONSE_PAYLOAD.getBytes())
+ .setStatusCode(200)
+ .build()))
+ .when(mMockHttpClient)
+ .performRequestAsyncWithRetry(any());
+
+ List<FederatedComputeEncryptionKey> keys =
+ mFederatedComputeEncryptionKeyManager
+ .fetchAndPersistActiveKeys(KEY_TYPE_ENCRYPTION, /* isScheduledJob= */ false)
+ .get();
+
+ assertThat(keys.size()).isGreaterThan(0);
+ }
+
+ @Test
+ public void testFetchAndPersistActiveKeys_scheduled_throws() {
+ doReturn(
+ Futures.immediateFailedFuture(
+ new ExecutionException(
+ "fetchAndPersistActiveKeys keys failed.",
+ new IllegalStateException("http 404"))))
+ .when(mMockHttpClient)
+ .performRequestAsyncWithRetry(any());
+
+ assertThrows(
+ ExecutionException.class,
+ () ->
+ mFederatedComputeEncryptionKeyManager
+ .fetchAndPersistActiveKeys(
+ KEY_TYPE_ENCRYPTION, /* isScheduledJob= */ true)
+ .get());
+ }
+
+ @Test
+ public void testFetchAndPersistActiveKeys_nonScheduled_throws() {
+ doReturn(
+ Futures.immediateFailedFuture(
+ new ExecutionException(
+ "fetchAndPersistActiveKeys keys failed.",
+ new IllegalStateException("http 404"))))
+ .when(mMockHttpClient)
+ .performRequestAsyncWithRetry(any());
+
+ assertThrows(
+ ExecutionException.class,
+ () ->
+ mFederatedComputeEncryptionKeyManager
+ .fetchAndPersistActiveKeys(
+ KEY_TYPE_ENCRYPTION, /* isScheduledJob= */ false)
+ .get());
+ }
+
+ @Test
+ public void testFetchAndPersistActiveKeys_scheduledNoDeletion() throws Exception {
+ doReturn(
+ Futures.immediateFuture(
+ new FederatedComputeHttpResponse.Builder()
+ .setHeaders(SAMPLE_RESPONSE_HEADER)
+ .setPayload(SAMPLE_RESPONSE_PAYLOAD.getBytes())
+ .setStatusCode(200)
+ .build()))
+ .when(mMockHttpClient)
+ .performRequestAsyncWithRetry(any());
+
+ mFederatedComputeEncryptionKeyManager
+ .fetchAndPersistActiveKeys(KEY_TYPE_ENCRYPTION, /* isScheduledJob= */ true)
+ .get();
+ List<FederatedComputeEncryptionKey> keys =
+ mEncryptionKeyDao.readFederatedComputeEncryptionKeysFromDatabase(
+ ""
+ /* selection= */ ,
+ new String[0]
+ /* selectionArgs= */ ,
+ ""
+ /* orderBy= */ ,
+ -1
+ /* count= */);
+
+ assertThat(keys.size()).isEqualTo(1);
+ assertThat(
+ keys.stream()
+ .map(FederatedComputeEncryptionKey::getKeyIdentifier)
+ .collect(Collectors.toList()))
+ .containsAtLeastElementsIn(List.of("0cc9b4c9-08bd"));
+ }
+
+ @Test
+ public void testFetchAndPersistActiveKeys_nonScheduledNoDeletion() throws Exception {
+ doReturn(
+ Futures.immediateFuture(
+ new FederatedComputeHttpResponse.Builder()
+ .setHeaders(SAMPLE_RESPONSE_HEADER)
+ .setPayload(SAMPLE_RESPONSE_PAYLOAD.getBytes())
+ .setStatusCode(200)
+ .build()))
+ .when(mMockHttpClient)
+ .performRequestAsyncWithRetry(any());
+
+ mFederatedComputeEncryptionKeyManager
+ .fetchAndPersistActiveKeys(KEY_TYPE_ENCRYPTION, /* isScheduledJob= */ false)
+ .get();
+ List<FederatedComputeEncryptionKey> keys =
+ mEncryptionKeyDao.readFederatedComputeEncryptionKeysFromDatabase(
+ ""
+ /* selection= */ ,
+ new String[0]
+ /* selectionArgs= */ ,
+ ""
+ /* orderBy= */ ,
+ -1
+ /* count= */);
+
+ assertThat(keys.size()).isEqualTo(1);
+ assertThat(
+ keys.stream()
+ .map(FederatedComputeEncryptionKey::getKeyIdentifier)
+ .collect(Collectors.toList()))
+ .containsAtLeastElementsIn(List.of("0cc9b4c9-08bd"));
+ }
+
+ @Test
+ public void testFetchAndPersistActiveKeys_scheduledWithDeletion() throws Exception {
+ doReturn(
+ Futures.immediateFuture(
+ new FederatedComputeHttpResponse.Builder()
+ .setHeaders(SAMPLE_RESPONSE_HEADER)
+ .setPayload(SAMPLE_RESPONSE_PAYLOAD.getBytes())
+ .setStatusCode(200)
+ .build()))
+ .when(mMockHttpClient)
+ .performRequestAsyncWithRetry(any());
+ long currentTime = mClock.currentTimeMillis();
+ mEncryptionKeyDao.insertEncryptionKey(
+ new FederatedComputeEncryptionKey.Builder()
+ .setKeyIdentifier("5161e286-63e5")
+ .setPublicKey("YuOorP14obQLqASrvqbkNxyijjcAUIDx/xeMGZOyykc")
+ .setKeyType(KEY_TYPE_ENCRYPTION)
+ .setCreationTime(currentTime)
+ .setExpiryTime(currentTime)
+ .build());
+
+ mFederatedComputeEncryptionKeyManager
+ .fetchAndPersistActiveKeys(KEY_TYPE_ENCRYPTION, /* isScheduledJob= */ true)
+ .get();
+
+ List<FederatedComputeEncryptionKey> keys =
+ mEncryptionKeyDao.readFederatedComputeEncryptionKeysFromDatabase(
+ ""
+ /* selection= */ ,
+ new String[0]
+ /* selectionArgs= */ ,
+ ""
+ /* orderBy= */ ,
+ -1
+ /* count= */);
+
+ assertThat(keys.size()).isEqualTo(1);
+ }
+
+ @Test
+ public void testFetchAndPersistActiveKeys_nonScheduledWithDeletion() throws Exception {
+ doReturn(
+ Futures.immediateFuture(
+ new FederatedComputeHttpResponse.Builder()
+ .setHeaders(SAMPLE_RESPONSE_HEADER)
+ .setPayload(SAMPLE_RESPONSE_PAYLOAD.getBytes())
+ .setStatusCode(200)
+ .build()))
+ .when(mMockHttpClient)
+ .performRequestAsyncWithRetry(any());
+ long currentTime = mClock.currentTimeMillis();
+ mEncryptionKeyDao.insertEncryptionKey(
+ new FederatedComputeEncryptionKey.Builder()
+ .setKeyIdentifier("5161e286-63e5")
+ .setPublicKey("YuOorP14obQLqASrvqbkNxyijjcAUIDx/xeMGZOyykc")
+ .setKeyType(KEY_TYPE_ENCRYPTION)
+ .setCreationTime(currentTime)
+ .setExpiryTime(currentTime)
+ .build());
+
+ mFederatedComputeEncryptionKeyManager
+ .fetchAndPersistActiveKeys(KEY_TYPE_ENCRYPTION, /* isScheduledJob= */ false)
+ .get();
+
+ List<FederatedComputeEncryptionKey> keys =
+ mEncryptionKeyDao.readFederatedComputeEncryptionKeysFromDatabase(
+ ""
+ /* selection= */ ,
+ new String[0]
+ /* selectionArgs= */ ,
+ ""
+ /* orderBy= */ ,
+ -1
+ /* count= */);
+
+ assertThat(keys.size()).isEqualTo(2);
+
+ List<FederatedComputeEncryptionKey> activeKeys = mEncryptionKeyDao.getLatestExpiryNKeys(2);
+ assertThat(activeKeys.size()).isEqualTo(1);
+ }
+
+ @Test
+ public void testGetOrFetchActiveKeys_fetch() {
+ doReturn(
+ Futures.immediateFuture(
+ new FederatedComputeHttpResponse.Builder()
+ .setHeaders(SAMPLE_RESPONSE_HEADER)
+ .setPayload(SAMPLE_RESPONSE_PAYLOAD.getBytes())
+ .setStatusCode(200)
+ .build()))
+ .when(mMockHttpClient)
+ .performRequestAsyncWithRetry(any());
+
+ List<FederatedComputeEncryptionKey> keys =
+ mFederatedComputeEncryptionKeyManager.getOrFetchActiveKeys(
+ KEY_TYPE_ENCRYPTION, /* keyCount= */ 2);
+
+ verify(mMockHttpClient, times(1)).performRequestAsyncWithRetry(any());
+ assertThat(keys.size()).isEqualTo(1);
+ }
+
+ @Test
+ public void testGetOrFetchActiveKeys_noFetch() {
+ long currentTime = mClock.currentTimeMillis();
+ mEncryptionKeyDao.insertEncryptionKey(
+ new FederatedComputeEncryptionKey.Builder()
+ .setKeyIdentifier("5161e286-63e5")
+ .setPublicKey("YuOorP14obQLqASrvqbkNxyijjcAUIDx/xeMGZOyykc")
+ .setKeyType(KEY_TYPE_ENCRYPTION)
+ .setCreationTime(currentTime)
+ .setExpiryTime(currentTime + 5000L)
+ .build());
+ doReturn(
+ Futures.immediateFuture(
+ new FederatedComputeHttpResponse.Builder()
+ .setHeaders(SAMPLE_RESPONSE_HEADER)
+ .setPayload(SAMPLE_RESPONSE_PAYLOAD.getBytes())
+ .setStatusCode(200)
+ .build()))
+ .when(mMockHttpClient)
+ .performRequestAsyncWithRetry(any());
+
+ List<FederatedComputeEncryptionKey> keys =
+ mFederatedComputeEncryptionKeyManager.getOrFetchActiveKeys(
+ KEY_TYPE_ENCRYPTION, /* keyCount= */ 2);
+
+ verify(mMockHttpClient, never()).performRequestAsyncWithRetry(any());
+ assertThat(keys.size()).isEqualTo(1);
+ }
+
+ @Test
+ public void testGetOrFetchActiveKeys_failure() {
+ doReturn(Futures.immediateFailedFuture(new InterruptedException()))
+ .when(mMockHttpClient)
+ .performRequestAsyncWithRetry(any());
+
+ List<FederatedComputeEncryptionKey> keys =
+ mFederatedComputeEncryptionKeyManager.getOrFetchActiveKeys(
+ KEY_TYPE_ENCRYPTION, /* keyCount= */ 2);
+
+ assertThat(keys.size()).isEqualTo(0);
+ verify(mMockHttpClient, times(1)).performRequestAsyncWithRetry(any());
+ }
+}
diff --git a/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/FederatedComputeHttpRequestTest.java b/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/FederatedComputeHttpRequestTest.java
index a504df65..fb5a339c 100644
--- a/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/FederatedComputeHttpRequestTest.java
+++ b/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/FederatedComputeHttpRequestTest.java
@@ -16,6 +16,9 @@
package com.android.federatedcompute.services.http;
+import static com.android.federatedcompute.services.http.HttpClientUtil.ACCEPT_ENCODING_HDR;
+import static com.android.federatedcompute.services.http.HttpClientUtil.GZIP_ENCODING_HDR;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
@@ -39,11 +42,7 @@ public final class FederatedComputeHttpRequestTest {
IllegalArgumentException.class,
() ->
FederatedComputeHttpRequest.create(
- "http://invalid.com",
- HttpMethod.GET,
- new HashMap<>(),
- PAYLOAD,
- /* useCompression= */ false));
+ "http://invalid.com", HttpMethod.GET, new HashMap<>(), PAYLOAD));
}
@Test
@@ -52,11 +51,7 @@ public final class FederatedComputeHttpRequestTest {
IllegalArgumentException.class,
() ->
FederatedComputeHttpRequest.create(
- "https://valid.com",
- HttpMethod.GET,
- new HashMap<>(),
- PAYLOAD,
- /* useCompression= */ false));
+ "https://valid.com", HttpMethod.GET, new HashMap<>(), PAYLOAD));
}
@Test
@@ -67,11 +62,7 @@ public final class FederatedComputeHttpRequestTest {
IllegalArgumentException.class,
() ->
FederatedComputeHttpRequest.create(
- "https://valid.com",
- HttpMethod.POST,
- headers,
- PAYLOAD,
- /* useCompression= */ false));
+ "https://valid.com", HttpMethod.POST, headers, PAYLOAD));
}
@Test
@@ -79,11 +70,7 @@ public final class FederatedComputeHttpRequestTest {
String expectedUri = "https://valid.com";
FederatedComputeHttpRequest request =
FederatedComputeHttpRequest.create(
- expectedUri,
- HttpMethod.GET,
- new HashMap<>(),
- HttpClientUtil.EMPTY_BODY,
- /* useCompression= */ false);
+ expectedUri, HttpMethod.GET, new HashMap<>(), HttpClientUtil.EMPTY_BODY);
assertThat(request.getUri()).isEqualTo(expectedUri);
assertThat(request.getHttpMethod()).isEqualTo(HttpMethod.GET);
@@ -99,27 +86,19 @@ public final class FederatedComputeHttpRequestTest {
FederatedComputeHttpRequest request =
FederatedComputeHttpRequest.create(
- expectedUri,
- HttpMethod.GET,
- expectedHeaders,
- HttpClientUtil.EMPTY_BODY,
- /* useCompression= */ false);
+ expectedUri, HttpMethod.GET, expectedHeaders, HttpClientUtil.EMPTY_BODY);
assertThat(request.getUri()).isEqualTo(expectedUri);
assertThat(request.getExtraHeaders()).isEqualTo(expectedHeaders);
}
@Test
- public void createPostRequestWithoutBody_valid() throws Exception {
+ public void createPostRequestWithoutBody_valid() {
String expectedUri = "https://valid.com";
FederatedComputeHttpRequest request =
FederatedComputeHttpRequest.create(
- expectedUri,
- HttpMethod.POST,
- new HashMap<>(),
- HttpClientUtil.EMPTY_BODY,
- /* useCompression= */ false);
+ expectedUri, HttpMethod.POST, new HashMap<>(), HttpClientUtil.EMPTY_BODY);
assertThat(request.getUri()).isEqualTo(expectedUri);
assertTrue(request.getExtraHeaders().isEmpty());
@@ -128,16 +107,12 @@ public final class FederatedComputeHttpRequestTest {
}
@Test
- public void createPostRequestWithBody_valid() throws Exception {
+ public void createPostRequestWithBody_valid() {
String expectedUri = "https://valid.com";
FederatedComputeHttpRequest request =
FederatedComputeHttpRequest.create(
- expectedUri,
- HttpMethod.POST,
- new HashMap<>(),
- PAYLOAD,
- /* useCompression= */ false);
+ expectedUri, HttpMethod.POST, new HashMap<>(), PAYLOAD);
assertThat(request.getUri()).isEqualTo(expectedUri);
assertThat(request.getHttpMethod()).isEqualTo(HttpMethod.POST);
@@ -145,18 +120,14 @@ public final class FederatedComputeHttpRequestTest {
}
@Test
- public void createPostRequestWithBodyHeader_valid() throws Exception {
+ public void createPostRequestWithBodyHeader_valid() {
String expectedUri = "https://valid.com";
HashMap<String, String> expectedHeaders = new HashMap<>();
expectedHeaders.put("Foo", "Bar");
FederatedComputeHttpRequest request =
FederatedComputeHttpRequest.create(
- expectedUri,
- HttpMethod.POST,
- expectedHeaders,
- PAYLOAD,
- /* useCompression= */ false);
+ expectedUri, HttpMethod.POST, expectedHeaders, PAYLOAD);
assertThat(request.getUri()).isEqualTo(expectedUri);
assertThat(request.getHttpMethod()).isEqualTo(HttpMethod.POST);
@@ -165,23 +136,19 @@ public final class FederatedComputeHttpRequestTest {
}
@Test
- public void createPostRequestWithCompression_valid() throws Exception {
+ public void createGetRequestWithAcceptCompression_valid() {
String expectedUri = "https://valid.com";
-
+ HashMap<String, String> headerList = new HashMap<>();
+ headerList.put(ACCEPT_ENCODING_HDR, GZIP_ENCODING_HDR);
FederatedComputeHttpRequest request =
FederatedComputeHttpRequest.create(
- expectedUri,
- HttpMethod.POST,
- new HashMap<>(),
- PAYLOAD,
- /* useCompression= */ true);
+ expectedUri, HttpMethod.POST, headerList, PAYLOAD);
assertThat(request.getUri()).isEqualTo(expectedUri);
assertThat(request.getHttpMethod()).isEqualTo(HttpMethod.POST);
- assertThat(request.getBody()).isEqualTo(HttpClientUtil.compressWithGzip(PAYLOAD));
HashMap<String, String> expectedHeaders = new HashMap<>();
- expectedHeaders.put(HttpClientUtil.CONTENT_ENCODING_HDR, HttpClientUtil.GZIP_ENCODING_HDR);
- expectedHeaders.put(HttpClientUtil.CONTENT_LENGTH_HDR, String.valueOf(42));
+ expectedHeaders.put(HttpClientUtil.CONTENT_LENGTH_HDR, String.valueOf(22));
+ expectedHeaders.put(ACCEPT_ENCODING_HDR, GZIP_ENCODING_HDR);
assertThat(request.getExtraHeaders()).isEqualTo(expectedHeaders);
}
}
diff --git a/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/FederatedComputeHttpResponseTest.java b/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/FederatedComputeHttpResponseTest.java
index 2497b19d..d9936631 100644
--- a/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/FederatedComputeHttpResponseTest.java
+++ b/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/FederatedComputeHttpResponseTest.java
@@ -16,6 +16,8 @@
package com.android.federatedcompute.services.http;
+import static com.android.federatedcompute.services.http.HttpClientUtil.OCTET_STREAM;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
@@ -29,6 +31,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -58,7 +61,7 @@ public final class FederatedComputeHttpResponseTest {
}
@Test
- public void testBuildWithMinimalRequiredValues() throws Exception {
+ public void testBuildWithMinimalRequiredValues() {
final int responseCode = 200;
FederatedComputeHttpResponse response =
new FederatedComputeHttpResponse.Builder().setStatusCode(responseCode).build();
@@ -75,4 +78,20 @@ public final class FederatedComputeHttpResponseTest {
.setPayload("payload".getBytes(UTF_8))
.build());
}
+
+ @Test
+ public void testGetBody_success() {
+ final byte[] uncompressedBody = "payload".getBytes(UTF_8);
+ Map<String, List<String>> expectedHeaders = new HashMap<>();
+ expectedHeaders.put(HttpClientUtil.CONTENT_TYPE_HDR, ImmutableList.of(OCTET_STREAM));
+
+ FederatedComputeHttpResponse response =
+ new FederatedComputeHttpResponse.Builder()
+ .setStatusCode(200)
+ .setPayload(uncompressedBody)
+ .setHeaders(expectedHeaders)
+ .build();
+
+ assertThat(response.getPayload()).isEqualTo(uncompressedBody);
+ }
}
diff --git a/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/HttpClientTest.java b/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/HttpClientTest.java
index 2674f2d3..c616ec21 100644
--- a/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/HttpClientTest.java
+++ b/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/HttpClientTest.java
@@ -20,8 +20,11 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -34,6 +37,7 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -47,9 +51,16 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
@RunWith(JUnit4.class)
public final class HttpClientTest {
+ public static final FederatedComputeHttpRequest DEFAULT_GET_REQUEST =
+ FederatedComputeHttpRequest.create(
+ "https://google.com",
+ HttpMethod.GET,
+ new HashMap<>(),
+ HttpClientUtil.EMPTY_BODY);
@Spy private HttpClient mHttpClient = new HttpClient();
@Rule public MockitoRule rule = MockitoJUnit.rule();
@Mock private HttpURLConnection mMockHttpURLConnection;
@@ -61,22 +72,14 @@ public final class HttpClientTest {
"https://google.com",
HttpMethod.POST,
new HashMap<>(),
- HttpClientUtil.EMPTY_BODY,
- false);
+ HttpClientUtil.EMPTY_BODY);
doThrow(new IOException()).when(mHttpClient).setup(ArgumentMatchers.any());
- assertThrows(IllegalArgumentException.class, () -> mHttpClient.performRequest(request));
+ assertThrows(IOException.class, () -> mHttpClient.performRequest(request));
}
@Test
public void testPerformGetRequestSuccess() throws Exception {
- FederatedComputeHttpRequest request =
- FederatedComputeHttpRequest.create(
- "https://google.com",
- HttpMethod.GET,
- new HashMap<>(),
- HttpClientUtil.EMPTY_BODY,
- false);
String successMessage = "Success!";
InputStream mockStream = new ByteArrayInputStream(successMessage.getBytes(UTF_8));
Map<String, List<String>> mockHeaders = new HashMap<>();
@@ -88,7 +91,7 @@ public final class HttpClientTest {
when(mMockHttpURLConnection.getContentLengthLong())
.thenReturn((long) successMessage.length());
- FederatedComputeHttpResponse response = mHttpClient.performRequest(request);
+ FederatedComputeHttpResponse response = mHttpClient.performRequest(DEFAULT_GET_REQUEST);
assertThat(response.getStatusCode()).isEqualTo(200);
assertThat(response.getHeaders()).isEqualTo(mockHeaders);
@@ -98,13 +101,6 @@ public final class HttpClientTest {
@Test
public void testPerformGetRequestFails() throws Exception {
String failureMessage = "FAIL!";
- FederatedComputeHttpRequest request =
- FederatedComputeHttpRequest.create(
- "https://google.com",
- HttpMethod.GET,
- new HashMap<>(),
- HttpClientUtil.EMPTY_BODY,
- false);
InputStream mockStream = new ByteArrayInputStream(failureMessage.getBytes(UTF_8));
when(mMockHttpURLConnection.getErrorStream()).thenReturn(mockStream);
when(mMockHttpURLConnection.getResponseCode()).thenReturn(503);
@@ -113,22 +109,78 @@ public final class HttpClientTest {
when(mMockHttpURLConnection.getContentLengthLong())
.thenReturn((long) failureMessage.length());
- FederatedComputeHttpResponse response = mHttpClient.performRequest(request);
+ FederatedComputeHttpResponse response = mHttpClient.performRequest(DEFAULT_GET_REQUEST);
+
+ assertThat(response.getStatusCode()).isEqualTo(503);
+ assertTrue(response.getHeaders().isEmpty());
+ assertThat(response.getPayload()).isEqualTo(failureMessage.getBytes(UTF_8));
+ }
+
+ @Test
+ public void testPerformGetRequestFailsWithRetry() throws Exception {
+ String failureMessage = "FAIL!";
+ when(mMockHttpURLConnection.getErrorStream())
+ .then(invocation -> new ByteArrayInputStream(failureMessage.getBytes(UTF_8)));
+ when(mMockHttpURLConnection.getResponseCode()).thenReturn(503);
+ when(mMockHttpURLConnection.getHeaderFields()).thenReturn(new HashMap<>());
+ when(mMockHttpURLConnection.getContentLengthLong())
+ .thenReturn((long) failureMessage.length());
+ doReturn(mMockHttpURLConnection).when(mHttpClient).setup(ArgumentMatchers.any());
+ FederatedComputeHttpResponse response =
+ mHttpClient.performRequestWithRetry(DEFAULT_GET_REQUEST);
+
+ verify(mHttpClient, times(3)).performRequest(DEFAULT_GET_REQUEST);
assertThat(response.getStatusCode()).isEqualTo(503);
assertTrue(response.getHeaders().isEmpty());
assertThat(response.getPayload()).isEqualTo(failureMessage.getBytes(UTF_8));
}
@Test
+ public void testPerformGetRequestSuccessWithRetry() throws Exception {
+ String failureMessage = "FAIL!";
+ InputStream mockStream = new ByteArrayInputStream(failureMessage.getBytes(UTF_8));
+ when(mMockHttpURLConnection.getErrorStream()).thenReturn(mockStream);
+ when(mMockHttpURLConnection.getResponseCode()).thenReturn(503);
+ when(mMockHttpURLConnection.getHeaderFields()).thenReturn(new HashMap<>());
+ HttpURLConnection mockSuccessfulHttpURLConnection = Mockito.mock(HttpURLConnection.class);
+ Map<String, List<String>> mockHeaders = new HashMap<>();
+ mockHeaders.put("Header1", Arrays.asList("Value1"));
+ when(mockSuccessfulHttpURLConnection.getOutputStream())
+ .thenReturn(new ByteArrayOutputStream());
+ when(mockSuccessfulHttpURLConnection.getResponseCode()).thenReturn(200);
+ when(mockSuccessfulHttpURLConnection.getHeaderFields()).thenReturn(mockHeaders);
+ final AtomicInteger countCall = new AtomicInteger();
+ doAnswer(
+ invocation -> {
+ int count = countCall.incrementAndGet();
+ if (count < 3) {
+ return mMockHttpURLConnection;
+ } else {
+ return mockSuccessfulHttpURLConnection;
+ }
+ })
+ .when(mHttpClient)
+ .setup(ArgumentMatchers.any());
+ when(mMockHttpURLConnection.getContentLengthLong())
+ .thenReturn((long) failureMessage.length());
+
+ FederatedComputeHttpResponse response =
+ mHttpClient.performRequestWithRetry(DEFAULT_GET_REQUEST);
+
+ verify(mHttpClient, times(3)).performRequest(DEFAULT_GET_REQUEST);
+ assertThat(response.getStatusCode()).isEqualTo(200);
+ assertThat(response.getHeaders()).isEqualTo(mockHeaders);
+ }
+
+ @Test
public void testPerformPostRequestSuccess() throws Exception {
FederatedComputeHttpRequest request =
FederatedComputeHttpRequest.create(
"https://google.com",
HttpMethod.POST,
new HashMap<>(),
- "payload".getBytes(UTF_8),
- false);
+ "payload".getBytes(UTF_8));
Map<String, List<String>> mockHeaders = new HashMap<>();
mockHeaders.put("Header1", Arrays.asList("Value1"));
when(mMockHttpURLConnection.getOutputStream()).thenReturn(new ByteArrayOutputStream());
diff --git a/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/HttpClientUtilTest.java b/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/HttpClientUtilTest.java
new file mode 100644
index 00000000..1f16aecc
--- /dev/null
+++ b/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/HttpClientUtilTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.federatedcompute.services.http;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.net.Uri;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+
+public class HttpClientUtilTest {
+ @Test
+ public void compress_uncompress_success() throws Exception {
+ String testUriPrefix =
+ "android.resource://com.android.ondevicepersonalization.federatedcomputetests/raw/";
+ Uri checkpointUri = Uri.parse(testUriPrefix + "federation_test_checkpoint_client");
+ Context context = ApplicationProvider.getApplicationContext();
+ InputStream in = context.getContentResolver().openInputStream(checkpointUri);
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ byte[] buf = new byte[4096];
+ int bytesRead;
+ while ((bytesRead = in.read(buf)) != -1) {
+ outputStream.write(buf, 0, bytesRead);
+ }
+ byte[] dataBeforeCompress = outputStream.toByteArray();
+
+ byte[] dataAfterCompress = HttpClientUtil.compressWithGzip(dataBeforeCompress);
+ assertThat(dataAfterCompress.length).isLessThan(dataBeforeCompress.length);
+
+ byte[] unzipData = HttpClientUtil.uncompressWithGzip(dataAfterCompress);
+ assertThat(unzipData).isEqualTo(dataBeforeCompress);
+ }
+}
diff --git a/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/HttpFederatedProtocolTest.java b/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/HttpFederatedProtocolTest.java
index fb1d95c4..e483cf22 100644
--- a/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/HttpFederatedProtocolTest.java
+++ b/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/HttpFederatedProtocolTest.java
@@ -16,31 +16,46 @@
package com.android.federatedcompute.services.http;
+import static com.android.federatedcompute.services.http.HttpClientUtil.ACCEPT_ENCODING_HDR;
+import static com.android.federatedcompute.services.http.HttpClientUtil.CONTENT_ENCODING_HDR;
import static com.android.federatedcompute.services.http.HttpClientUtil.CONTENT_LENGTH_HDR;
import static com.android.federatedcompute.services.http.HttpClientUtil.CONTENT_TYPE_HDR;
+import static com.android.federatedcompute.services.http.HttpClientUtil.GZIP_ENCODING_HDR;
+import static com.android.federatedcompute.services.http.HttpClientUtil.ODP_IDEMPOTENCY_KEY;
import static com.android.federatedcompute.services.http.HttpClientUtil.PROTOBUF_CONTENT_TYPE;
+import static com.android.federatedcompute.services.http.HttpClientUtil.compressWithGzip;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.util.concurrent.Futures.immediateFuture;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+
+import android.content.Context;
+import android.net.Uri;
+
+import androidx.test.core.app.ApplicationProvider;
import com.android.federatedcompute.services.http.HttpClientUtil.HttpMethod;
import com.android.federatedcompute.services.testutils.TrainingTestUtil;
import com.android.federatedcompute.services.training.util.ComputationResult;
-import com.google.common.io.Files;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.intelligence.fcp.client.FLRunnerResult;
import com.google.intelligence.fcp.client.FLRunnerResult.ContributionResult;
-import com.google.internal.federated.plan.ClientOnlyPlan;
import com.google.internal.federatedcompute.v1.ClientVersion;
import com.google.internal.federatedcompute.v1.RejectionInfo;
import com.google.internal.federatedcompute.v1.Resource;
+import com.google.internal.federatedcompute.v1.ResourceCapabilities;
+import com.google.internal.federatedcompute.v1.ResourceCompressionFormat;
import com.google.ondevicepersonalization.federatedcompute.proto.CreateTaskAssignmentRequest;
import com.google.ondevicepersonalization.federatedcompute.proto.CreateTaskAssignmentResponse;
import com.google.ondevicepersonalization.federatedcompute.proto.ReportResultRequest;
@@ -53,7 +68,7 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
+import org.junit.runners.Parameterized;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
@@ -61,11 +76,14 @@ import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.io.File;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutionException;
-@RunWith(JUnit4.class)
+@RunWith(Parameterized.class)
public final class HttpFederatedProtocolTest {
private static final String TASK_ASSIGNMENT_TARGET_URI = "https://test-server.com/";
private static final String PLAN_URI = "https://fake.uri/plan";
@@ -85,18 +103,21 @@ public final class HttpFederatedProtocolTest {
private static final String OCTET_STREAM = "application/octet-stream";
private static final FLRunnerResult FL_RUNNER_SUCCESS_RESULT =
FLRunnerResult.newBuilder().setContributionResult(ContributionResult.SUCCESS).build();
+
private static final FLRunnerResult FL_RUNNER_FAIL_RESULT =
FLRunnerResult.newBuilder().setContributionResult(ContributionResult.FAIL).build();
- private static final FederatedComputeHttpResponse CHECKPOINT_HTTP_RESPONSE =
- new FederatedComputeHttpResponse.Builder()
- .setStatusCode(200)
- .setPayload(CHECKPOINT)
- .build();
-
- private static final CreateTaskAssignmentRequest START_TASK_ASSIGNMENT_REQUEST =
- CreateTaskAssignmentRequest.newBuilder()
- .setClientVersion(ClientVersion.newBuilder().setVersionCode(CLIENT_VERSION))
- .build();
+ private static final CreateTaskAssignmentRequest
+ START_TASK_ASSIGNMENT_REQUEST_WITH_COMPRESSION =
+ CreateTaskAssignmentRequest.newBuilder()
+ .setClientVersion(
+ ClientVersion.newBuilder().setVersionCode(CLIENT_VERSION))
+ .setResourceCapabilities(
+ ResourceCapabilities.newBuilder()
+ .addSupportedCompressionFormats(
+ ResourceCompressionFormat
+ .RESOURCE_COMPRESSION_FORMAT_GZIP)
+ .build())
+ .build();
private static final FederatedComputeHttpResponse SUCCESS_EMPTY_HTTP_RESPONSE =
new FederatedComputeHttpResponse.Builder().setStatusCode(200).build();
@@ -105,6 +126,15 @@ public final class HttpFederatedProtocolTest {
@Rule public MockitoRule rule = MockitoJUnit.rule();
@Mock private HttpClient mMockHttpClient;
+
+ @Parameterized.Parameter(0)
+ public boolean mSupportCompression;
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] {{false}, {true}});
+ }
+
private HttpFederatedProtocol mHttpFederatedProtocol;
@Before
@@ -128,21 +158,39 @@ public final class HttpFederatedProtocolTest {
// Verify task assignment request.
FederatedComputeHttpRequest actualStartTaskAssignmentRequest = actualHttpRequests.get(0);
assertThat(actualStartTaskAssignmentRequest.getUri()).isEqualTo(START_TASK_ASSIGNMENT_URI);
- assertThat(actualStartTaskAssignmentRequest.getBody())
- .isEqualTo(START_TASK_ASSIGNMENT_REQUEST.toByteArray());
+
assertThat(actualStartTaskAssignmentRequest.getHttpMethod()).isEqualTo(HttpMethod.POST);
HashMap<String, String> expectedHeaders = new HashMap<>();
- expectedHeaders.put(CONTENT_LENGTH_HDR, String.valueOf(18));
+ assertThat(actualStartTaskAssignmentRequest.getBody())
+ .isEqualTo(START_TASK_ASSIGNMENT_REQUEST_WITH_COMPRESSION.toByteArray());
+ expectedHeaders.put(CONTENT_LENGTH_HDR, String.valueOf(23));
expectedHeaders.put(CONTENT_TYPE_HDR, PROTOBUF_CONTENT_TYPE);
assertThat(actualStartTaskAssignmentRequest.getExtraHeaders())
- .containsExactlyEntriesIn(expectedHeaders);
+ .containsAtLeastEntriesIn(expectedHeaders);
+ assertThat(actualStartTaskAssignmentRequest.getExtraHeaders()).hasSize(3);
+ String idempotencyKey =
+ actualStartTaskAssignmentRequest.getExtraHeaders().get(ODP_IDEMPOTENCY_KEY);
+ assertNotNull(idempotencyKey);
+ String timestamp = idempotencyKey.split(" - ")[0];
+ assertThat(Long.parseLong(timestamp)).isLessThan(System.currentTimeMillis());
+
+ // Verify fetch resource request.
+ FederatedComputeHttpRequest actualFetchResourceRequest = actualHttpRequests.get(1);
+ ImmutableSet<String> resourceUris = ImmutableSet.of(PLAN_URI, CHECKPOINT_URI);
+ assertTrue(resourceUris.contains(actualFetchResourceRequest.getUri()));
+ expectedHeaders = new HashMap<>();
+ if (mSupportCompression) {
+ expectedHeaders.put(ACCEPT_ENCODING_HDR, GZIP_ENCODING_HDR);
+ }
+ assertThat(actualFetchResourceRequest.getExtraHeaders()).isEqualTo(expectedHeaders);
}
@Test
- public void testCreateTaskAssignmentFailed() throws Exception {
+ public void testCreateTaskAssignmentFailed() {
FederatedComputeHttpResponse httpResponse =
new FederatedComputeHttpResponse.Builder().setStatusCode(404).build();
- when(mMockHttpClient.performRequestAsync(any())).thenReturn(immediateFuture(httpResponse));
+ when(mMockHttpClient.performRequestAsyncWithRetry(any()))
+ .thenReturn(immediateFuture(httpResponse));
ExecutionException exception =
assertThrows(
@@ -166,15 +214,13 @@ public final class HttpFederatedProtocolTest {
.setStatusCode(200)
.setPayload(createTaskAssignmentResponse.toByteArray())
.build();
- when(mMockHttpClient.performRequestAsync(any())).thenReturn(immediateFuture(httpResponse));
+ when(mMockHttpClient.performRequestAsyncWithRetry(any()))
+ .thenReturn(immediateFuture(httpResponse));
- ExecutionException exception =
- assertThrows(
- ExecutionException.class,
- () -> mHttpFederatedProtocol.issueCheckin().get());
+ CheckinResult checkinResult = mHttpFederatedProtocol.issueCheckin().get();
- assertThat(exception.getCause()).isInstanceOf(IllegalStateException.class);
- assertThat(exception.getCause()).hasMessageThat().isEqualTo("Device rejected by server.");
+ assertThat(checkinResult.getRejectionInfo()).isNotNull();
+ assertThat(checkinResult.getRejectionInfo()).isEqualTo(RejectionInfo.getDefaultInstance());
}
@Test
@@ -186,7 +232,7 @@ public final class HttpFederatedProtocolTest {
setUpHttpFederatedProtocol(
createStartTaskAssignmentHttpResponse(),
planHttpResponse,
- CHECKPOINT_HTTP_RESPONSE,
+ checkpointHttpResponse(),
/** reportResultHttpResponse= */
null,
/** uploadResultHttpResponse= */
@@ -276,7 +322,12 @@ public final class HttpFederatedProtocolTest {
assertThat(actualDataUploadRequest.getUri()).isEqualTo(UPLOAD_LOCATION_URI);
assertThat(acutalReportResultRequest.getHttpMethod()).isEqualTo(HttpMethod.PUT);
expectedHeaders = new HashMap<>();
- expectedHeaders.put(CONTENT_LENGTH_HDR, String.valueOf(17));
+ if (mSupportCompression) {
+ expectedHeaders.put(CONTENT_LENGTH_HDR, String.valueOf(339));
+ expectedHeaders.put(CONTENT_ENCODING_HDR, GZIP_ENCODING_HDR);
+ } else {
+ expectedHeaders.put(CONTENT_LENGTH_HDR, String.valueOf(31846));
+ }
expectedHeaders.put(CONTENT_TYPE_HDR, OCTET_STREAM);
assertThat(actualDataUploadRequest.getExtraHeaders()).isEqualTo(expectedHeaders);
}
@@ -291,7 +342,7 @@ public final class HttpFederatedProtocolTest {
setUpHttpFederatedProtocol(
createStartTaskAssignmentHttpResponse(),
createPlanHttpResponse(),
- CHECKPOINT_HTTP_RESPONSE,
+ checkpointHttpResponse(),
reportResultHttpResponse,
null);
@@ -315,7 +366,7 @@ public final class HttpFederatedProtocolTest {
setUpHttpFederatedProtocol(
createStartTaskAssignmentHttpResponse(),
createPlanHttpResponse(),
- CHECKPOINT_HTTP_RESPONSE,
+ checkpointHttpResponse(),
createReportResultHttpResponse(),
uploadResultHttpResponse);
@@ -330,41 +381,50 @@ public final class HttpFederatedProtocolTest {
}
private String createOutputCheckpointFile() throws Exception {
+ String testUriPrefix =
+ "android.resource://com.android.ondevicepersonalization.federatedcomputetests/raw/";
File outputCheckpointFile = File.createTempFile("output", ".ckp");
- Files.write("output checkpoint".getBytes(), outputCheckpointFile);
+ Context context = ApplicationProvider.getApplicationContext();
+ Uri checkpointUri = Uri.parse(testUriPrefix + "federation_test_checkpoint_client");
+ InputStream in = context.getContentResolver().openInputStream(checkpointUri);
+ java.nio.file.Files.copy(in, outputCheckpointFile.toPath(), REPLACE_EXISTING);
+ in.close();
outputCheckpointFile.deleteOnExit();
return outputCheckpointFile.getAbsolutePath();
}
private FederatedComputeHttpResponse createPlanHttpResponse() {
- ClientOnlyPlan clientOnlyPlan = TrainingTestUtil.createFederatedAnalyticClientPlan();
+ byte[] clientOnlyPlan = TrainingTestUtil.createFederatedAnalyticClientPlan().toByteArray();
return new FederatedComputeHttpResponse.Builder()
.setStatusCode(200)
- .setPayload(clientOnlyPlan.toByteArray())
+ .setHeaders(mSupportCompression ? compressionHeaderList() : new HashMap<>())
+ .setPayload(mSupportCompression ? compressWithGzip(clientOnlyPlan) : clientOnlyPlan)
.build();
}
- private void setUpHttpFederatedProtocol() throws Exception {
- FederatedComputeHttpResponse checkpointHttpResponse =
- new FederatedComputeHttpResponse.Builder()
- .setStatusCode(200)
- .setPayload(CHECKPOINT)
- .build();
+ private void setUpHttpFederatedProtocol() {
setUpHttpFederatedProtocol(
createStartTaskAssignmentHttpResponse(),
createPlanHttpResponse(),
- checkpointHttpResponse,
+ checkpointHttpResponse(),
createReportResultHttpResponse(),
SUCCESS_EMPTY_HTTP_RESPONSE);
}
+ private FederatedComputeHttpResponse checkpointHttpResponse() {
+ return new FederatedComputeHttpResponse.Builder()
+ .setStatusCode(200)
+ .setPayload(mSupportCompression ? compressWithGzip(CHECKPOINT) : CHECKPOINT)
+ .setHeaders(mSupportCompression ? compressionHeaderList() : new HashMap<>())
+ .build();
+ }
+
private void setUpHttpFederatedProtocol(
FederatedComputeHttpResponse createTaskAssignmentResponse,
FederatedComputeHttpResponse planHttpResponse,
FederatedComputeHttpResponse checkpointHttpResponse,
FederatedComputeHttpResponse reportResultHttpResponse,
- FederatedComputeHttpResponse uploadResultHttpResponse)
- throws Exception {
+ FederatedComputeHttpResponse uploadResultHttpResponse) {
doAnswer(
invocation -> {
FederatedComputeHttpRequest httpRequest = invocation.getArgument(0);
@@ -383,13 +443,25 @@ public final class HttpFederatedProtocolTest {
return immediateFuture(SUCCESS_EMPTY_HTTP_RESPONSE);
})
.when(mMockHttpClient)
- .performRequestAsync(mHttpRequestCaptor.capture());
+ .performRequestAsyncWithRetry(mHttpRequestCaptor.capture());
+ }
+
+ private HashMap<String, List<String>> compressionHeaderList() {
+ HashMap<String, List<String>> headerList = new HashMap<>();
+ headerList.put(CONTENT_ENCODING_HDR, ImmutableList.of(GZIP_ENCODING_HDR));
+ headerList.put(CONTENT_TYPE_HDR, ImmutableList.of(OCTET_STREAM));
+ return headerList;
}
- private FederatedComputeHttpResponse createReportResultHttpResponse() throws Exception {
+ private FederatedComputeHttpResponse createReportResultHttpResponse() {
UploadInstruction.Builder uploadInstruction =
UploadInstruction.newBuilder().setUploadLocation(UPLOAD_LOCATION_URI);
uploadInstruction.putExtraRequestHeaders(CONTENT_TYPE_HDR, OCTET_STREAM);
+ if (mSupportCompression) {
+ uploadInstruction.putExtraRequestHeaders(CONTENT_ENCODING_HDR, GZIP_ENCODING_HDR);
+ uploadInstruction.setCompressionFormat(
+ ResourceCompressionFormat.RESOURCE_COMPRESSION_FORMAT_GZIP);
+ }
ReportResultResponse reportResultResponse =
ReportResultResponse.newBuilder()
.setUploadInstruction(uploadInstruction.build())
@@ -400,11 +472,27 @@ public final class HttpFederatedProtocolTest {
.build();
}
- private FederatedComputeHttpResponse createStartTaskAssignmentHttpResponse() throws Exception {
+ private FederatedComputeHttpResponse createStartTaskAssignmentHttpResponse() {
CreateTaskAssignmentResponse createTaskAssignmentResponse =
createCreateTaskAssignmentResponse(
- Resource.newBuilder().setUri(PLAN_URI).build(),
- Resource.newBuilder().setUri(CHECKPOINT_URI).build());
+ Resource.newBuilder()
+ .setUri(PLAN_URI)
+ .setCompressionFormat(
+ mSupportCompression
+ ? ResourceCompressionFormat
+ .RESOURCE_COMPRESSION_FORMAT_GZIP
+ : ResourceCompressionFormat
+ .RESOURCE_COMPRESSION_FORMAT_UNSPECIFIED)
+ .build(),
+ Resource.newBuilder()
+ .setUri(CHECKPOINT_URI)
+ .setCompressionFormat(
+ mSupportCompression
+ ? ResourceCompressionFormat
+ .RESOURCE_COMPRESSION_FORMAT_GZIP
+ : ResourceCompressionFormat
+ .RESOURCE_COMPRESSION_FORMAT_UNSPECIFIED)
+ .build());
return new FederatedComputeHttpResponse.Builder()
.setStatusCode(200)
diff --git a/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/ProtocolRequestCreatorTest.java b/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/ProtocolRequestCreatorTest.java
index 1fa86e40..afcc97c9 100644
--- a/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/ProtocolRequestCreatorTest.java
+++ b/tests/federatedcomputetests/src/com/android/federatedcompute/services/http/ProtocolRequestCreatorTest.java
@@ -43,7 +43,7 @@ public final class ProtocolRequestCreatorTest {
@Test
public void testCreateProtobufEncodedRequest() {
ProtocolRequestCreator requestCreator =
- new ProtocolRequestCreator(REQUEST_BASE_URI, new HashMap<String, String>(), false);
+ new ProtocolRequestCreator(REQUEST_BASE_URI, new HashMap<String, String>());
FederatedComputeHttpRequest request =
requestCreator.createProtoRequest(
@@ -63,9 +63,7 @@ public final class ProtocolRequestCreatorTest {
IllegalArgumentException exception =
assertThrows(
IllegalArgumentException.class,
- () ->
- ProtocolRequestCreator.create(
- ForwardingInfo.getDefaultInstance(), false));
+ () -> ProtocolRequestCreator.create(ForwardingInfo.getDefaultInstance()));
assertThat(exception)
.hasMessageThat()
@@ -75,7 +73,7 @@ public final class ProtocolRequestCreatorTest {
@Test
public void testCreateProtocolRequestInvalidSuffix() {
ProtocolRequestCreator requestCreator =
- new ProtocolRequestCreator(REQUEST_BASE_URI, new HashMap<String, String>(), false);
+ new ProtocolRequestCreator(REQUEST_BASE_URI, new HashMap<String, String>());
IllegalArgumentException exception =
assertThrows(
@@ -93,8 +91,7 @@ public final class ProtocolRequestCreatorTest {
public void testCreateProtocolRequestWithForwardingInfo() {
ForwardingInfo forwardingInfo =
ForwardingInfo.newBuilder().setTargetUriPrefix(AGGREGATION_TARGET_URI).build();
- ProtocolRequestCreator requestCreator =
- ProtocolRequestCreator.create(forwardingInfo, false);
+ ProtocolRequestCreator requestCreator = ProtocolRequestCreator.create(forwardingInfo);
FederatedComputeHttpRequest request =
requestCreator.createProtoRequest(
@@ -106,7 +103,7 @@ public final class ProtocolRequestCreatorTest {
@Test
public void testCreateProtoRequest() {
ProtocolRequestCreator requestCreator =
- new ProtocolRequestCreator(REQUEST_BASE_URI, new HashMap<String, String>(), false);
+ new ProtocolRequestCreator(REQUEST_BASE_URI, new HashMap<String, String>());
FederatedComputeHttpRequest request =
requestCreator.createProtoRequest(
diff --git a/tests/federatedcomputetests/src/com/android/federatedcompute/services/training/FederatedComputeWorkerTest.java b/tests/federatedcomputetests/src/com/android/federatedcompute/services/training/FederatedComputeWorkerTest.java
index 34179225..71fe9998 100644
--- a/tests/federatedcomputetests/src/com/android/federatedcompute/services/training/FederatedComputeWorkerTest.java
+++ b/tests/federatedcomputetests/src/com/android/federatedcompute/services/training/FederatedComputeWorkerTest.java
@@ -22,12 +22,15 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
import static com.google.common.util.concurrent.Futures.immediateFuture;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
@@ -59,6 +62,7 @@ import com.android.federatedcompute.services.testutils.TrainingTestUtil;
import com.android.federatedcompute.services.training.ResultCallbackHelper.CallbackResult;
import com.android.federatedcompute.services.training.aidl.IIsolatedTrainingService;
import com.android.federatedcompute.services.training.aidl.ITrainingResultCallback;
+import com.android.federatedcompute.services.training.util.ComputationResult;
import com.android.federatedcompute.services.training.util.TrainingConditionsChecker;
import com.android.federatedcompute.services.training.util.TrainingConditionsChecker.Condition;
@@ -76,15 +80,19 @@ import com.google.intelligence.fcp.client.engine.TaskRetry;
import com.google.internal.federated.plan.ClientOnlyPlan;
import com.google.internal.federated.plan.ClientPhase;
import com.google.internal.federated.plan.TensorflowSpec;
+import com.google.internal.federatedcompute.v1.RejectionInfo;
+import com.google.internal.federatedcompute.v1.RetryWindow;
import com.google.ondevicepersonalization.federatedcompute.proto.TaskAssignment;
import com.google.protobuf.Any;
import com.google.protobuf.ByteString;
+import com.google.protobuf.Duration;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
@@ -121,6 +129,15 @@ public final class FederatedComputeWorkerTest {
createTempFile("input", ".ckp"),
TrainingTestUtil.createFederatedAnalyticClientPlan(),
TaskAssignment.newBuilder().setTaskName(TASK_NAME).build());
+
+ public static final RejectionInfo REJECTION_INFO =
+ RejectionInfo.newBuilder()
+ .setRetryWindow(
+ RetryWindow.newBuilder()
+ .setDelayMin(Duration.newBuilder().setSeconds(3600).build())
+ .build())
+ .build();
+ private static final CheckinResult REJECTION_CHECKIN_RESULT = new CheckinResult(REJECTION_INFO);
private static final FLRunnerResult FL_RUNNER_FAILURE_RESULT =
FLRunnerResult.newBuilder().setContributionResult(ContributionResult.FAIL).build();
@@ -172,9 +189,9 @@ public final class FederatedComputeWorkerTest {
@Mock FederatedComputeJobManager mMockJobManager;
private Context mContext;
private FederatedComputeWorker mSpyWorker;
- @Mock private HttpFederatedProtocol mMockHttpFederatedProtocol;
+ private HttpFederatedProtocol mSpyHttpFederatedProtocol;
@Mock private ComputationRunner mMockComputationRunner;
- @Mock private ResultCallbackHelper mMockResultCallbackHelper;
+ private ResultCallbackHelper mSpyResultCallbackHelper;
private static byte[] createTrainingConstraints(
boolean requiresSchedulerIdle,
@@ -201,6 +218,10 @@ public final class FederatedComputeWorkerTest {
@Before
public void doBeforeEachTest() throws Exception {
mContext = ApplicationProvider.getApplicationContext();
+ mSpyHttpFederatedProtocol =
+ Mockito.spy(
+ HttpFederatedProtocol.create(SERVER_ADDRESS, "1.0.0.1", POPULATION_NAME));
+ mSpyResultCallbackHelper = Mockito.spy(new ResultCallbackHelper(mContext));
mSpyWorker =
Mockito.spy(
new FederatedComputeWorker(
@@ -208,14 +229,15 @@ public final class FederatedComputeWorkerTest {
mMockJobManager,
mTrainingConditionsChecker,
mMockComputationRunner,
- mMockResultCallbackHelper,
+ mSpyResultCallbackHelper,
new TestInjector()));
when(mTrainingConditionsChecker.checkAllConditionsForFlTraining(any()))
.thenReturn(EnumSet.noneOf(Condition.class));
- when(mMockResultCallbackHelper.callHandleResult(eq(TASK_NAME), any(), any()))
- .thenReturn(Futures.immediateFuture(CallbackResult.SUCCESS));
+ doReturn(Futures.immediateFuture(CallbackResult.SUCCESS))
+ .when(mSpyResultCallbackHelper)
+ .callHandleResult(eq(TASK_NAME), any(), any());
when(mMockJobManager.onTrainingStarted(anyInt())).thenReturn(FEDERATED_TRAINING_TASK_1);
- doReturn(mMockHttpFederatedProtocol)
+ doReturn(mSpyHttpFederatedProtocol)
.when(mSpyWorker)
.getHttpFederatedProtocol(anyString(), anyString());
when(mMockComputationRunner.runTaskWithNativeRunner(
@@ -263,10 +285,10 @@ public final class FederatedComputeWorkerTest {
new ExecutionException(
"issue checkin failed",
new IllegalStateException("http 404"))))
- .when(mMockHttpFederatedProtocol)
+ .when(mSpyHttpFederatedProtocol)
.issueCheckin();
doReturn(FluentFuture.from(immediateFuture(null)))
- .when(mMockHttpFederatedProtocol)
+ .when(mSpyHttpFederatedProtocol)
.reportResult(any());
assertThrows(ExecutionException.class, () -> mSpyWorker.startTrainingRun(JOB_ID).get());
@@ -278,11 +300,58 @@ public final class FederatedComputeWorkerTest {
}
@Test
+ public void testCheckinWithRejection() throws Exception {
+ setUpExampleStoreService();
+ doReturn(
+ immediateFuture(REJECTION_CHECKIN_RESULT))
+ .when(mSpyHttpFederatedProtocol)
+ .issueCheckin();
+
+ FLRunnerResult result = mSpyWorker.startTrainingRun(JOB_ID).get();
+
+ assertNull(result);
+ verify(mMockJobManager)
+ .onTrainingCompleted(
+ anyInt(), anyString(), any(), any(), eq(ContributionResult.FAIL));
+ mSpyWorker.finish(null, ContributionResult.FAIL, false);
+ }
+
+ @Test
+ public void testReportResultWithRejection() throws Exception {
+ setUpExampleStoreService();
+ doReturn(immediateFuture(FA_CHECKIN_RESULT))
+ .when(mSpyHttpFederatedProtocol)
+ .issueCheckin();
+ doReturn(FluentFuture.from(immediateFuture(REJECTION_INFO)))
+ .when(mSpyHttpFederatedProtocol)
+ .reportResult(any());
+ doCallRealMethod().when(mSpyResultCallbackHelper).callHandleResult(any(), any(), any());
+ ArgumentCaptor<ComputationResult> computationResultCaptor =
+ ArgumentCaptor.forClass(ComputationResult.class);
+
+
+ FLRunnerResult result = mSpyWorker.startTrainingRun(JOB_ID).get();
+
+ assertNull(result);
+ verify(mMockJobManager)
+ .onTrainingCompleted(
+ anyInt(), anyString(), any(), any(), eq(ContributionResult.FAIL));
+ verify(mSpyResultCallbackHelper)
+ .callHandleResult(any(), any(), computationResultCaptor.capture());
+ ComputationResult computationResult = computationResultCaptor.getValue();
+ assertNotNull(computationResult.getFlRunnerResult());
+ assertEquals(
+ ContributionResult.FAIL,
+ computationResult.getFlRunnerResult().getContributionResult());
+ mSpyWorker.finish(null, ContributionResult.FAIL, false);
+ }
+
+ @Test
public void testReportResultFails_throwsException() throws Exception {
setUpExampleStoreService();
doReturn(immediateFuture(FA_CHECKIN_RESULT))
- .when(mMockHttpFederatedProtocol)
+ .when(mSpyHttpFederatedProtocol)
.issueCheckin();
doReturn(
FluentFuture.from(
@@ -290,7 +359,7 @@ public final class FederatedComputeWorkerTest {
new ExecutionException(
"report result failed",
new IllegalStateException("http 404")))))
- .when(mMockHttpFederatedProtocol)
+ .when(mSpyHttpFederatedProtocol)
.reportResult(any());
assertThrows(ExecutionException.class, () -> mSpyWorker.startTrainingRun(JOB_ID).get());
@@ -305,10 +374,10 @@ public final class FederatedComputeWorkerTest {
@Test
public void testBindToExampleStoreFails_throwsException() throws Exception {
setUpHttpFederatedProtocol(FL_CHECKIN_RESULT);
-
// Mock failure bind to ExampleStoreService.
doReturn(null).when(mSpyWorker).getExampleStoreService(anyString());
- doNothing().when(mSpyWorker).unbindFromExampleStoreService();
+ ArgumentCaptor<ComputationResult> computationResultCaptor =
+ ArgumentCaptor.forClass(ComputationResult.class);
assertThrows(ExecutionException.class, () -> mSpyWorker.startTrainingRun(JOB_ID).get());
@@ -317,6 +386,13 @@ public final class FederatedComputeWorkerTest {
.onTrainingCompleted(
anyInt(), anyString(), any(), any(), eq(ContributionResult.FAIL));
verify(mSpyWorker, times(0)).unbindFromExampleStoreService();
+ verify(mSpyHttpFederatedProtocol, times(1))
+ .reportResult(computationResultCaptor.capture());
+ ComputationResult computationResult = computationResultCaptor.getValue();
+ assertNotNull(computationResult.getFlRunnerResult());
+ assertEquals(
+ ContributionResult.FAIL,
+ computationResult.getFlRunnerResult().getContributionResult());
}
@Test
@@ -348,14 +424,52 @@ public final class FederatedComputeWorkerTest {
}
@Test
+ public void testRunFAComputationThrows() throws Exception {
+ setUpExampleStoreService();
+ setUpHttpFederatedProtocol(FA_CHECKIN_RESULT);
+ doReturn(FluentFuture.from(immediateFuture(null)))
+ .when(mSpyHttpFederatedProtocol)
+ .reportResult(any());
+ when(mMockComputationRunner.runTaskWithNativeRunner(
+ anyString(),
+ anyString(),
+ anyString(),
+ anyString(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any()))
+ .thenThrow(new RuntimeException("Test failures!"));
+ ArgumentCaptor<ComputationResult> computationResultCaptor =
+ ArgumentCaptor.forClass(ComputationResult.class);
+
+ assertThrows(ExecutionException.class, () -> mSpyWorker.startTrainingRun(JOB_ID).get());
+
+ mSpyWorker.finish(null, ContributionResult.FAIL, false);
+ verify(mMockJobManager)
+ .onTrainingCompleted(
+ anyInt(), anyString(), any(), any(), eq(ContributionResult.FAIL));
+ verify(mSpyWorker).unbindFromExampleStoreService();
+ verify(mSpyHttpFederatedProtocol, times(1))
+ .reportResult(computationResultCaptor.capture());
+ ComputationResult computationResult = computationResultCaptor.getValue();
+ assertNotNull(computationResult.getFlRunnerResult());
+ assertEquals(
+ ContributionResult.FAIL,
+ computationResult.getFlRunnerResult().getContributionResult());
+ }
+
+ @Test
public void testPublishToResultHandlingServiceFails_returnsSuccess() throws Exception {
setUpExampleStoreService();
setUpHttpFederatedProtocol(FA_CHECKIN_RESULT);
// Mock publish to ResultHandlingService fails which is best effort and should not affect
// final result.
- when(mMockResultCallbackHelper.callHandleResult(eq(TASK_NAME), any(), any()))
- .thenReturn(Futures.immediateFuture(CallbackResult.FAIL));
+ doReturn(Futures.immediateFuture(CallbackResult.FAIL))
+ .when(mSpyResultCallbackHelper)
+ .callHandleResult(eq(TASK_NAME), any(), any());
FLRunnerResult result = mSpyWorker.startTrainingRun(JOB_ID).get();
assertThat(result.getContributionResult()).isEqualTo(ContributionResult.SUCCESS);
@@ -375,12 +489,12 @@ public final class FederatedComputeWorkerTest {
// Mock publish to ResultHandlingService throws exception which is best effort and should
// not affect final result.
- when(mMockResultCallbackHelper.callHandleResult(eq(TASK_NAME), any(), any()))
- .thenReturn(
- immediateFailedFuture(
- new ExecutionException(
- "ResultHandlingService fail",
- new IllegalStateException("can't bind to service"))));
+ doReturn(immediateFailedFuture(
+ new ExecutionException(
+ "ResultHandlingService fail",
+ new IllegalStateException("can't bind to service"))))
+ .when(mSpyResultCallbackHelper)
+ .callHandleResult(eq(TASK_NAME), any(), any());
FLRunnerResult result = mSpyWorker.startTrainingRun(JOB_ID).get();
assertThat(result.getContributionResult()).isEqualTo(ContributionResult.SUCCESS);
@@ -390,7 +504,7 @@ public final class FederatedComputeWorkerTest {
.onTrainingCompleted(
anyInt(), anyString(), any(), any(), eq(ContributionResult.SUCCESS));
verify(mSpyWorker).unbindFromExampleStoreService();
- verify(mMockResultCallbackHelper).callHandleResult(eq(TASK_NAME), any(), any());
+ verify(mSpyResultCallbackHelper).callHandleResult(eq(TASK_NAME), any(), any());
}
@Test
@@ -408,7 +522,7 @@ public final class FederatedComputeWorkerTest {
@Test
public void testBindToIsolatedTrainingServiceFail_returnsFail() throws Exception {
doReturn(immediateFuture(FL_CHECKIN_RESULT))
- .when(mMockHttpFederatedProtocol)
+ .when(mSpyHttpFederatedProtocol)
.issueCheckin();
setUpExampleStoreService();
@@ -489,9 +603,9 @@ public final class FederatedComputeWorkerTest {
}
private void setUpHttpFederatedProtocol(CheckinResult checkinResult) {
- doReturn(immediateFuture(checkinResult)).when(mMockHttpFederatedProtocol).issueCheckin();
+ doReturn(immediateFuture(checkinResult)).when(mSpyHttpFederatedProtocol).issueCheckin();
doReturn(FluentFuture.from(immediateFuture(null)))
- .when(mMockHttpFederatedProtocol)
+ .when(mSpyHttpFederatedProtocol)
.reportResult(any());
}
diff --git a/tests/frameworktests/src/android/adservices/ondevicepersonalization/IsolatedServiceTest.java b/tests/frameworktests/src/android/adservices/ondevicepersonalization/IsolatedServiceTest.java
index a7c11057..caef0bdb 100644
--- a/tests/frameworktests/src/android/adservices/ondevicepersonalization/IsolatedServiceTest.java
+++ b/tests/frameworktests/src/android/adservices/ondevicepersonalization/IsolatedServiceTest.java
@@ -83,8 +83,8 @@ public class IsolatedServiceTest {
public void testOnExecute() throws Exception {
PersistableBundle appParams = new PersistableBundle();
appParams.putString("x", "y");
- ExecuteInput input =
- new ExecuteInput.Builder()
+ ExecuteInputParcel input =
+ new ExecuteInputParcel.Builder()
.setAppPackageName("com.testapp")
.setAppParams(appParams)
.build();
@@ -97,8 +97,8 @@ public class IsolatedServiceTest {
mBinder.onRequest(Constants.OP_EXECUTE, params, new TestServiceCallback());
mLatch.await();
assertTrue(mSelectContentCalled);
- ExecuteOutput result =
- mCallbackResult.getParcelable(Constants.EXTRA_RESULT, ExecuteOutput.class);
+ ExecuteOutputParcel result =
+ mCallbackResult.getParcelable(Constants.EXTRA_RESULT, ExecuteOutputParcel.class);
assertEquals(5, result.getRequestLogRecord().getRows().get(0).getAsInteger("a").intValue());
assertEquals("123", result.getRenderingConfigs().get(0).getKeys().get(0));
}
@@ -107,8 +107,8 @@ public class IsolatedServiceTest {
public void testOnExecutePropagatesError() throws Exception {
PersistableBundle appParams = new PersistableBundle();
appParams.putInt("error", 1); // Trigger an error in the service.
- ExecuteInput input =
- new ExecuteInput.Builder()
+ ExecuteInputParcel input =
+ new ExecuteInputParcel.Builder()
.setAppPackageName("com.testapp")
.setAppParams(appParams)
.build();
@@ -126,7 +126,7 @@ public class IsolatedServiceTest {
@Test
public void testOnExecuteWithoutAppParams() throws Exception {
- ExecuteInput input = new ExecuteInput.Builder().setAppPackageName("com.testapp").build();
+ ExecuteInputParcel input = new ExecuteInputParcel.Builder().setAppPackageName("com.testapp").build();
Bundle params = new Bundle();
params.putParcelable(Constants.EXTRA_INPUT, input);
params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
@@ -163,7 +163,7 @@ public class IsolatedServiceTest {
@Test
public void testOnExecuteThrowsIfDataAccessServiceMissing() throws Exception {
- ExecuteInput input = new ExecuteInput.Builder().setAppPackageName("com.testapp").build();
+ ExecuteInputParcel input = new ExecuteInputParcel.Builder().setAppPackageName("com.testapp").build();
Bundle params = new Bundle();
params.putBinder(
Constants.EXTRA_FEDERATED_COMPUTE_SERVICE_BINDER,
@@ -178,7 +178,7 @@ public class IsolatedServiceTest {
@Test
public void testOnExecuteThrowsIfFederatedComputeServiceMissing() throws Exception {
- ExecuteInput input = new ExecuteInput.Builder().setAppPackageName("com.testapp").build();
+ ExecuteInputParcel input = new ExecuteInputParcel.Builder().setAppPackageName("com.testapp").build();
Bundle params = new Bundle();
params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
params.putParcelable(Constants.EXTRA_INPUT, input);
@@ -191,7 +191,7 @@ public class IsolatedServiceTest {
@Test
public void testOnExecuteThrowsIfCallbackMissing() throws Exception {
- ExecuteInput input = new ExecuteInput.Builder().setAppPackageName("com.testapp").build();
+ ExecuteInputParcel input = new ExecuteInputParcel.Builder().setAppPackageName("com.testapp").build();
Bundle params = new Bundle();
params.putParcelable(Constants.EXTRA_INPUT, input);
params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
@@ -218,9 +218,9 @@ public class IsolatedServiceTest {
mBinder.onRequest(Constants.OP_DOWNLOAD, params, new TestServiceCallback());
mLatch.await();
assertTrue(mOnDownloadCalled);
- DownloadCompletedOutput result =
+ DownloadCompletedOutputParcel result =
mCallbackResult.getParcelable(
- Constants.EXTRA_RESULT, DownloadCompletedOutput.class);
+ Constants.EXTRA_RESULT, DownloadCompletedOutputParcel.class);
assertEquals("12", result.getRetainedKeys().get(0));
}
@@ -297,8 +297,8 @@ public class IsolatedServiceTest {
@Test
public void testOnRender() throws Exception {
- RenderInput input =
- new RenderInput.Builder()
+ RenderInputParcel input =
+ new RenderInputParcel.Builder()
.setRenderingConfig(
new RenderingConfig.Builder().addKey("a").addKey("b").build())
.build();
@@ -308,15 +308,15 @@ public class IsolatedServiceTest {
mBinder.onRequest(Constants.OP_RENDER, params, new TestServiceCallback());
mLatch.await();
assertTrue(mOnRenderCalled);
- RenderOutput result =
- mCallbackResult.getParcelable(Constants.EXTRA_RESULT, RenderOutput.class);
+ RenderOutputParcel result =
+ mCallbackResult.getParcelable(Constants.EXTRA_RESULT, RenderOutputParcel.class);
assertEquals("htmlstring", result.getContent());
}
@Test
public void testOnRenderPropagatesError() throws Exception {
- RenderInput input =
- new RenderInput.Builder()
+ RenderInputParcel input =
+ new RenderInputParcel.Builder()
.setRenderingConfig(
new RenderingConfig.Builder()
.addKey("z") // Trigger error in service.
@@ -353,8 +353,8 @@ public class IsolatedServiceTest {
@Test
public void testOnRenderThrowsIfDataAccessServiceMissing() throws Exception {
- RenderInput input =
- new RenderInput.Builder()
+ RenderInputParcel input =
+ new RenderInputParcel.Builder()
.setRenderingConfig(
new RenderingConfig.Builder().addKey("a").addKey("b").build())
.build();
@@ -369,8 +369,8 @@ public class IsolatedServiceTest {
@Test
public void testOnRenderThrowsIfCallbackMissing() throws Exception {
- RenderInput input =
- new RenderInput.Builder()
+ RenderInputParcel input =
+ new RenderInputParcel.Builder()
.setRenderingConfig(
new RenderingConfig.Builder().addKey("a").addKey("b").build())
.build();
@@ -385,29 +385,30 @@ public class IsolatedServiceTest {
}
@Test
- public void testOnWebViewEvent() throws Exception {
+ public void testOnEvent() throws Exception {
Bundle params = new Bundle();
params.putParcelable(
Constants.EXTRA_INPUT,
- new EventInput.Builder().setParameters(PersistableBundle.EMPTY).build());
+ new EventInputParcel.Builder().setParameters(PersistableBundle.EMPTY).build());
params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
mBinder.onRequest(Constants.OP_WEB_VIEW_EVENT, params, new TestServiceCallback());
mLatch.await();
assertTrue(mOnEventCalled);
- EventOutput result =
- mCallbackResult.getParcelable(Constants.EXTRA_RESULT, EventOutput.class);
+ EventOutputParcel result =
+ mCallbackResult.getParcelable(Constants.EXTRA_RESULT, EventOutputParcel.class);
assertEquals(1, result.getEventLogRecord().getType());
assertEquals(2, result.getEventLogRecord().getRowIndex());
}
@Test
- public void testOnWebViewEventPropagatesError() throws Exception {
+ public void testOnEventPropagatesError() throws Exception {
PersistableBundle eventParams = new PersistableBundle();
// Input value 9999 will trigger an error in the mock service.
eventParams.putInt(EVENT_TYPE_KEY, 9999);
Bundle params = new Bundle();
params.putParcelable(
- Constants.EXTRA_INPUT, new EventInput.Builder().setParameters(eventParams).build());
+ Constants.EXTRA_INPUT,
+ new EventInputParcel.Builder().setParameters(eventParams).build());
params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
mBinder.onRequest(Constants.OP_WEB_VIEW_EVENT, params, new TestServiceCallback());
mLatch.await();
@@ -441,7 +442,7 @@ public class IsolatedServiceTest {
Bundle params = new Bundle();
params.putParcelable(
Constants.EXTRA_INPUT,
- new EventInput.Builder().setParameters(PersistableBundle.EMPTY).build());
+ new EventInputParcel.Builder().setParameters(PersistableBundle.EMPTY).build());
assertThrows(
NullPointerException.class,
() -> {
@@ -455,7 +456,7 @@ public class IsolatedServiceTest {
Bundle params = new Bundle();
params.putParcelable(
Constants.EXTRA_INPUT,
- new EventInput.Builder().setParameters(PersistableBundle.EMPTY).build());
+ new EventInputParcel.Builder().setParameters(PersistableBundle.EMPTY).build());
params.putBinder(Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new TestDataAccessService());
assertThrows(
NullPointerException.class,
@@ -465,10 +466,10 @@ public class IsolatedServiceTest {
}
@Test
- public void testOnTrainingExample() throws Exception {
+ public void testOnTrainingExamples() throws Exception {
JoinedLogRecord joinedLogRecord = new JoinedLogRecord.Builder().build();
- TrainingExampleInput input =
- new TrainingExampleInput.Builder()
+ TrainingExamplesInputParcel input =
+ new TrainingExamplesInputParcel.Builder()
.setPopulationName("")
.setTaskName("")
.setResumptionToken(new byte[] {0})
@@ -479,9 +480,9 @@ public class IsolatedServiceTest {
mBinder.onRequest(Constants.OP_TRAINING_EXAMPLE, params, new TestServiceCallback());
mLatch.await();
assertTrue(mOnTrainingExampleCalled);
- TrainingExampleOutputParcel result =
+ TrainingExamplesOutputParcel result =
mCallbackResult.getParcelable(
- Constants.EXTRA_RESULT, TrainingExampleOutputParcel.class);
+ Constants.EXTRA_RESULT, TrainingExamplesOutputParcel.class);
List<byte[]> examples = result.getTrainingExamples().getList();
List<byte[]> tokens = result.getResumptionTokens().getList();
assertEquals(1, examples.size());
@@ -503,8 +504,8 @@ public class IsolatedServiceTest {
@Test
public void testOnTrainingExampleThrowsIfDataAccessServiceMissing() throws Exception {
JoinedLogRecord joinedLogRecord = new JoinedLogRecord.Builder().build();
- TrainingExampleInput input =
- new TrainingExampleInput.Builder()
+ TrainingExamplesInputParcel input =
+ new TrainingExamplesInputParcel.Builder()
.setPopulationName("")
.setTaskName("")
.setResumptionToken(new byte[] {0})
@@ -522,8 +523,8 @@ public class IsolatedServiceTest {
@Test
public void testOnTrainingExampleThrowsIfCallbackMissing() throws Exception {
JoinedLogRecord joinedLogRecord = new JoinedLogRecord.Builder().build();
- TrainingExampleInput input =
- new TrainingExampleInput.Builder()
+ TrainingExamplesInputParcel input =
+ new TrainingExamplesInputParcel.Builder()
.setPopulationName("")
.setTaskName("")
.setResumptionToken(new byte[] {0})
@@ -608,15 +609,15 @@ public class IsolatedServiceTest {
}
@Override
- public void onTrainingExample(
- TrainingExampleInput input, Consumer<TrainingExampleOutput> consumer) {
+ public void onTrainingExamples(
+ TrainingExamplesInput input, Consumer<TrainingExamplesOutput> consumer) {
mOnTrainingExampleCalled = true;
List<byte[]> examples = new ArrayList<>();
examples.add(new byte[] {12});
List<byte[]> tokens = new ArrayList<>();
tokens.add(new byte[] {13});
consumer.accept(
- new TrainingExampleOutput.Builder()
+ new TrainingExamplesOutput.Builder()
.setTrainingExamples(examples)
.setResumptionTokens(tokens)
.build());
diff --git a/tests/frameworktests/src/android/adservices/ondevicepersonalization/LogReaderTest.java b/tests/frameworktests/src/android/adservices/ondevicepersonalization/LogReaderTest.java
index 6397a44e..e0056d0a 100644
--- a/tests/frameworktests/src/android/adservices/ondevicepersonalization/LogReaderTest.java
+++ b/tests/frameworktests/src/android/adservices/ondevicepersonalization/LogReaderTest.java
@@ -35,6 +35,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@@ -56,7 +57,8 @@ public class LogReaderTest {
@Test
public void testGetRequestsSuccess() {
- List<RequestLogRecord> result = mLogReader.getRequests(10, 100);
+ List<RequestLogRecord> result = mLogReader.getRequests(
+ Instant.ofEpochMilli(10), Instant.ofEpochMilli(100));
assertEquals(2, result.size());
assertEquals(1, result.get(0).getRows().size());
assertEquals((int) (result.get(0).getRows().get(0).getAsInteger("a")), 1);
@@ -67,25 +69,38 @@ public class LogReaderTest {
}
@Test
+ public void testGetRequestsNullTimeError() {
+ assertThrows(NullPointerException.class, () -> mLogReader.getRequests(
+ null, Instant.ofEpochMilli(100)));
+ assertThrows(NullPointerException.class, () -> mLogReader.getRequests(
+ Instant.ofEpochMilli(100), null));
+ }
+
+ @Test
public void testGetRequestsError() {
// Triggers an expected error in the mock service.
- assertThrows(IllegalStateException.class, () -> mLogReader.getRequests(7, 100));
+ assertThrows(IllegalStateException.class, () -> mLogReader.getRequests(
+ Instant.ofEpochMilli(7), Instant.ofEpochMilli(100)));
}
@Test
public void testGetRequestsNegativeTimeError() {
- assertThrows(IllegalArgumentException.class, () -> mLogReader.getRequests(-1, 100));
+ assertThrows(IllegalArgumentException.class, () -> mLogReader.getRequests(
+ Instant.ofEpochMilli(-1), Instant.ofEpochMilli(100)));
}
@Test
public void testGetRequestsBadTimeRangeError() {
- assertThrows(IllegalArgumentException.class, () -> mLogReader.getRequests(100, 100));
- assertThrows(IllegalArgumentException.class, () -> mLogReader.getRequests(1000, 100));
+ assertThrows(IllegalArgumentException.class, () -> mLogReader.getRequests(
+ Instant.ofEpochMilli(100), Instant.ofEpochMilli(100)));
+ assertThrows(IllegalArgumentException.class, () -> mLogReader.getRequests(
+ Instant.ofEpochMilli(1000), Instant.ofEpochMilli(100)));
}
@Test
public void testGetJoinedEventsSuccess() {
- List<EventLogRecord> result = mLogReader.getJoinedEvents(10, 100);
+ List<EventLogRecord> result = mLogReader.getJoinedEvents(
+ Instant.ofEpochMilli(10), Instant.ofEpochMilli(100));
assertEquals(2, result.size());
assertEquals(result.get(0).getTimeMillis(), 30);
assertEquals(result.get(0).getRequestLogRecord().getTimeMillis(), 20);
@@ -102,18 +117,30 @@ public class LogReaderTest {
@Test
public void testGetJoinedEventsError() {
// Triggers an expected error in the mock service.
- assertThrows(IllegalStateException.class, () -> mLogReader.getJoinedEvents(7, 100));
+ assertThrows(IllegalStateException.class, () -> mLogReader.getJoinedEvents(
+ Instant.ofEpochMilli(7), Instant.ofEpochMilli(100)));
+ }
+
+ @Test
+ public void testGetJoinedEventsNullTimeError() {
+ assertThrows(NullPointerException.class, () -> mLogReader.getJoinedEvents(
+ null, Instant.ofEpochMilli(100)));
+ assertThrows(NullPointerException.class, () -> mLogReader.getJoinedEvents(
+ Instant.ofEpochMilli(100), null));
}
@Test
public void testGetJoinedEventsNegativeTimeError() {
- assertThrows(IllegalArgumentException.class, () -> mLogReader.getJoinedEvents(-1, 100));
+ assertThrows(IllegalArgumentException.class, () -> mLogReader.getJoinedEvents(
+ Instant.ofEpochMilli(-1), Instant.ofEpochMilli(100)));
}
@Test
public void testGetJoinedEventsInputError() {
- assertThrows(IllegalArgumentException.class, () -> mLogReader.getJoinedEvents(100, 100));
- assertThrows(IllegalArgumentException.class, () -> mLogReader.getJoinedEvents(1000, 100));
+ assertThrows(IllegalArgumentException.class, () -> mLogReader.getJoinedEvents(
+ Instant.ofEpochMilli(100), Instant.ofEpochMilli(100)));
+ assertThrows(IllegalArgumentException.class, () -> mLogReader.getJoinedEvents(
+ Instant.ofEpochMilli(1000), Instant.ofEpochMilli(100)));
}
public static class LocalDataService extends IDataAccessService.Stub {
diff --git a/tests/frameworktests/src/android/adservices/ondevicepersonalization/OnDevicePersonalizationFrameworkClassesTest.java b/tests/frameworktests/src/android/adservices/ondevicepersonalization/OnDevicePersonalizationFrameworkClassesTest.java
index eac96f6c..258ecac1 100644
--- a/tests/frameworktests/src/android/adservices/ondevicepersonalization/OnDevicePersonalizationFrameworkClassesTest.java
+++ b/tests/frameworktests/src/android/adservices/ondevicepersonalization/OnDevicePersonalizationFrameworkClassesTest.java
@@ -37,23 +37,46 @@ import java.util.ArrayList;
@RunWith(AndroidJUnit4.class)
public class OnDevicePersonalizationFrameworkClassesTest {
/**
+ * Tests that the ExecuteInput object serializes correctly.
+ */
+ @Test
+ public void testExecuteInput() {
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putInt("a", 5);
+ ExecuteInputParcel data = new ExecuteInputParcel.Builder()
+ .setAppPackageName("com.example.test")
+ .setAppParams(bundle)
+ .build();
+
+ Parcel parcel = Parcel.obtain();
+ data.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ ExecuteInputParcel data2 = ExecuteInputParcel.CREATOR.createFromParcel(parcel);
+ ExecuteInput result = new ExecuteInput(data2);
+
+ assertEquals("com.example.test", result.getAppPackageName());
+ assertEquals(5, result.getAppParams().getInt("a"));
+ }
+
+ /**
* Tests that the ExecuteOutput object serializes correctly.
*/
@Test
public void testExecuteOutput() {
ContentValues row = new ContentValues();
row.put("a", 5);
- ExecuteOutput result =
+ ExecuteOutput data =
new ExecuteOutput.Builder()
.setRequestLogRecord(new RequestLogRecord.Builder().addRow(row).build())
.addRenderingConfig(new RenderingConfig.Builder().addKey("abc").build())
.addEventLogRecord(new EventLogRecord.Builder().setType(1).build())
.build();
+ ExecuteOutputParcel result = new ExecuteOutputParcel(data);
Parcel parcel = Parcel.obtain();
result.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
- ExecuteOutput result2 = ExecuteOutput.CREATOR.createFromParcel(parcel);
+ ExecuteOutputParcel result2 = ExecuteOutputParcel.CREATOR.createFromParcel(parcel);
assertEquals(
5, result2.getRequestLogRecord().getRows().get(0).getAsInteger("a").intValue());
@@ -62,16 +85,41 @@ public class OnDevicePersonalizationFrameworkClassesTest {
}
/**
+ * Tests that the RenderInput object serializes correctly.
+ */
+ @Test
+ public void testRenderInput() {
+ RenderInputParcel data = new RenderInputParcel.Builder()
+ .setWidth(10)
+ .setHeight(20)
+ .setRenderingConfigIndex(5)
+ .setRenderingConfig(new RenderingConfig.Builder().addKey("abc").build())
+ .build();
+
+ Parcel parcel = Parcel.obtain();
+ data.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ RenderInputParcel data2 = RenderInputParcel.CREATOR.createFromParcel(parcel);
+ RenderInput result = new RenderInput(data2);
+
+ assertEquals(10, result.getWidth());
+ assertEquals(20, result.getHeight());
+ assertEquals(5, result.getRenderingConfigIndex());
+ assertEquals("abc", result.getRenderingConfig().getKeys().get(0));
+ }
+
+ /**
* Tests that the RenderOutput object serializes correctly.
*/
@Test
public void testRenderOutput() {
- RenderOutput result = new RenderOutput.Builder().setContent("abc").build();
+ RenderOutput data = new RenderOutput.Builder().setContent("abc").build();
+ RenderOutputParcel result = new RenderOutputParcel(data);
Parcel parcel = Parcel.obtain();
result.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
- RenderOutput result2 = RenderOutput.CREATOR.createFromParcel(parcel);
+ RenderOutputParcel result2 = RenderOutputParcel.CREATOR.createFromParcel(parcel);
assertEquals("abc", result2.getContent());
}
@@ -81,16 +129,17 @@ public class OnDevicePersonalizationFrameworkClassesTest {
*/
@Test
public void teetDownloadCompletedOutput() {
- DownloadCompletedOutput result = new DownloadCompletedOutput.Builder()
+ DownloadCompletedOutput data = new DownloadCompletedOutput.Builder()
.addRetainedKey("abc").addRetainedKey("def").build();
+ DownloadCompletedOutputParcel result =
+ new DownloadCompletedOutputParcel(data);
Parcel parcel = Parcel.obtain();
result.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
- DownloadCompletedOutput result2 =
- DownloadCompletedOutput.CREATOR.createFromParcel(parcel);
+ DownloadCompletedOutputParcel result2 =
+ DownloadCompletedOutputParcel.CREATOR.createFromParcel(parcel);
- assertEquals(result, result2);
assertEquals("abc", result2.getRetainedKeys().get(0));
assertEquals("def", result2.getRetainedKeys().get(1));
}
@@ -105,7 +154,7 @@ public class OnDevicePersonalizationFrameworkClassesTest {
ArrayList<ContentValues> rows = new ArrayList<>();
rows.add(new ContentValues());
rows.get(0).put("a", 5);
- EventInput result = new EventInput.Builder()
+ EventInputParcel data = new EventInputParcel.Builder()
.setParameters(params)
.setRequestLogRecord(
new RequestLogRecord.Builder()
@@ -114,13 +163,14 @@ public class OnDevicePersonalizationFrameworkClassesTest {
.build();
Parcel parcel = Parcel.obtain();
- result.writeToParcel(parcel, 0);
+ data.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
- EventInput result2 = EventInput.CREATOR.createFromParcel(parcel);
+ EventInputParcel data2 = EventInputParcel.CREATOR.createFromParcel(parcel);
+ EventInput result = new EventInput(data2);
- assertEquals(3, result2.getParameters().getInt("x"));
+ assertEquals(3, result.getParameters().getInt("x"));
assertEquals(
- 5, result2.getRequestLogRecord().getRows().get(0).getAsInteger("a").intValue());
+ 5, result.getRequestLogRecord().getRows().get(0).getAsInteger("a").intValue());
}
/**
@@ -130,7 +180,7 @@ public class OnDevicePersonalizationFrameworkClassesTest {
public void testEventOutput() {
ContentValues data = new ContentValues();
data.put("a", 3);
- EventOutput result = new EventOutput.Builder()
+ EventOutput output = new EventOutput.Builder()
.setEventLogRecord(
new EventLogRecord.Builder()
.setType(5)
@@ -138,13 +188,13 @@ public class OnDevicePersonalizationFrameworkClassesTest {
.setData(data)
.build())
.build();
+ EventOutputParcel result = new EventOutputParcel(output);
Parcel parcel = Parcel.obtain();
result.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
- EventOutput result2 = EventOutput.CREATOR.createFromParcel(parcel);
+ EventOutputParcel result2 = EventOutputParcel.CREATOR.createFromParcel(parcel);
- assertEquals(result, result2);
assertEquals(5, result2.getEventLogRecord().getType());
assertEquals(6, result2.getEventLogRecord().getRowIndex());
assertEquals(3, result2.getEventLogRecord().getData().getAsInteger("a").intValue());
diff --git a/tests/perftests/scenarios/src/android/federatedcompute/test/scenario/federatedcompute/ScheduleAndForceTraining.java b/tests/perftests/scenarios/src/android/federatedcompute/test/scenario/federatedcompute/ScheduleAndForceTraining.java
index d66ca093..61466b5a 100644
--- a/tests/perftests/scenarios/src/android/federatedcompute/test/scenario/federatedcompute/ScheduleAndForceTraining.java
+++ b/tests/perftests/scenarios/src/android/federatedcompute/test/scenario/federatedcompute/ScheduleAndForceTraining.java
@@ -37,8 +37,9 @@ public class ScheduleAndForceTraining {
/** Prepare the device before entering the test class */
@BeforeClass
- public static void prepareDevice() {
+ public static void prepareDevice() throws IOException {
TestHelper.initialize();
+ TestHelper.killRunningProcess();
}
@Before
diff --git a/tests/perftests/scenarios/src/android/federatedcompute/test/scenario/federatedcompute/TestHelper.java b/tests/perftests/scenarios/src/android/federatedcompute/test/scenario/federatedcompute/TestHelper.java
index 1a4fc6a3..c8ff1f88 100644
--- a/tests/perftests/scenarios/src/android/federatedcompute/test/scenario/federatedcompute/TestHelper.java
+++ b/tests/perftests/scenarios/src/android/federatedcompute/test/scenario/federatedcompute/TestHelper.java
@@ -70,6 +70,34 @@ public class TestHelper {
executeShellCommand(
"am broadcast -a android.intent.action.BOOT_COMPLETED -p "
+ "com.google.android.federatedcompute");
+ executeShellCommand(
+ "cmd jobscheduler run -f "
+ + "com.google.android.ondevicepersonalization.services 1000");
+ SystemClock.sleep(5000);
+ executeShellCommand(
+ "cmd jobscheduler run -f "
+ + "com.google.android.ondevicepersonalization.services 1006");
+ SystemClock.sleep(5000);
+ executeShellCommand(
+ "cmd jobscheduler run -f "
+ + "com.google.android.ondevicepersonalization.services 1003");
+ SystemClock.sleep(5000);
+ executeShellCommand(
+ "cmd jobscheduler run -f "
+ + "com.google.android.ondevicepersonalization.services 1004");
+ SystemClock.sleep(5000);
+ }
+
+ /** Kill running processes to get performance measurement under cold start */
+ public static void killRunningProcess() throws IOException {
+ executeShellCommand("am kill com.google.android.ondevicepersonalization.services");
+ executeShellCommand("am kill com.google.android.ondevicepersonalization.services:"
+ + "com.android.ondevicepersonalization."
+ + "libraries.plugin.internal.PluginExecutorService");
+ executeShellCommand("am kill com.google.android.federatedcompute");
+ executeShellCommand("am kill com.google.android.federatedcompute:"
+ + "com.android.federatedcompute.services.training.IsolatedTrainingService");
+ SystemClock.sleep(2000);
}
/** Commands to return device to original state */
diff --git a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/DownloadHelper.java b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/DownloadHelper.java
index 1bda1e99..d181f82f 100644
--- a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/DownloadHelper.java
+++ b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/DownloadHelper.java
@@ -73,6 +73,15 @@ public class DownloadHelper {
+ "com.google.android.ondevicepersonalization.services 1006");
SystemClock.sleep(5000);
}
+
+ /** Kill running processes to get performance measurement under cold start */
+ public static void killRunningProcess() throws IOException {
+ executeShellCommand("am kill com.google.android.ondevicepersonalization.services");
+ executeShellCommand("am kill com.google.android.ondevicepersonalization.services:"
+ + "com.android.ondevicepersonalization."
+ + "libraries.plugin.internal.PluginExecutorService");
+ SystemClock.sleep(2000);
+ }
public static void pressHome() {
getUiDevice().pressHome();
}
diff --git a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/DownloadVendorData.java b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/DownloadVendorData.java
index a97a9fc2..2a9f3526 100644
--- a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/DownloadVendorData.java
+++ b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/DownloadVendorData.java
@@ -36,6 +36,7 @@ public class DownloadVendorData {
@BeforeClass
public static void prepareDevice() throws IOException {
DownloadHelper.initialize();
+ DownloadHelper.killRunningProcess();
}
@Before
public void setUp() throws IOException {
diff --git a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/ReportConversion.java b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/ReportConversion.java
new file mode 100644
index 00000000..32b72c7b
--- /dev/null
+++ b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/ReportConversion.java
@@ -0,0 +1,65 @@
+/*
+ * 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.ondevicepersonalization.test.scenario.ondevicepersonalization;
+
+import android.platform.test.scenario.annotation.Scenario;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.IOException;
+
+@Scenario
+@RunWith(JUnit4.class)
+public class ReportConversion {
+
+ private TestAppHelper mTestAppHelper = new TestAppHelper();
+
+ /** Prepare the device before entering the test class */
+ @BeforeClass
+ public static void prepareDevice() throws IOException {
+ TestAppHelper.initialize();
+ TestAppHelper.killRunningProcess();
+ }
+
+ @Before
+ public void setup() throws IOException {
+ mTestAppHelper.openApp();
+ }
+
+ @Test
+ public void testReportConversion() throws IOException {
+ mTestAppHelper.clickGetAd();
+ mTestAppHelper.verifyRenderedView();
+ mTestAppHelper.clickAd("Google!");
+ mTestAppHelper.openApp();
+ mTestAppHelper.inputSourceAdId("ad1");
+ mTestAppHelper.resetLogBuffer();
+ mTestAppHelper.clickReportConversion();
+ mTestAppHelper.verifyConversionReported();
+ }
+
+ /** Return device to original state after test exeuction */
+ @AfterClass
+ public static void tearDown() throws IOException {
+ TestAppHelper.goToHomeScreen();
+ TestAppHelper.wrapUp();
+ }
+}
diff --git a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/RequestAd.java b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/RequestAd.java
index 71f22bca..cba70b28 100644
--- a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/RequestAd.java
+++ b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/RequestAd.java
@@ -36,6 +36,7 @@ public class RequestAd {
@BeforeClass
public static void prepareDevice() throws IOException {
TestAppHelper.initialize();
+ TestAppHelper.killRunningProcess();
}
@Before
diff --git a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/RequestAdAndClickAd.java b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/RequestAdAndClickAd.java
index 9cd3d359..6b2a878c 100644
--- a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/RequestAdAndClickAd.java
+++ b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/RequestAdAndClickAd.java
@@ -36,6 +36,7 @@ public class RequestAdAndClickAd {
@BeforeClass
public static void prepareDevice() throws IOException {
TestAppHelper.initialize();
+ TestAppHelper.killRunningProcess();
}
@Before
diff --git a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/RequestAdWithTestAppRotations.java b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/RequestAdWithTestAppRotations.java
index 5e899f4d..5e0b5e0f 100644
--- a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/RequestAdWithTestAppRotations.java
+++ b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/RequestAdWithTestAppRotations.java
@@ -37,6 +37,7 @@ public class RequestAdWithTestAppRotations {
@BeforeClass
public static void prepareDevice() throws IOException {
TestAppHelper.initialize();
+ TestAppHelper.killRunningProcess();
}
@Before
@@ -49,25 +50,32 @@ public class RequestAdWithTestAppRotations {
mTestAppHelper.clickGetAd();
mTestAppHelper.verifyRenderedView();
- // Rotate to landscape layout
+ // Rotate to landscape layout and get Ad
mTestAppHelper.setOrientationLandscape();
mTestAppHelper.clickGetAd();
mTestAppHelper.verifyRenderedView();
- // Rotate to portrait layout
+ // Rotate to portrait layout and get Ad
mTestAppHelper.setOrientationPortrait();
mTestAppHelper.clickGetAd();
mTestAppHelper.verifyRenderedView();
- // Rotate to landscape layout
+ // Rotate to landscape layout and do nothing
mTestAppHelper.setOrientationLandscape();
- mTestAppHelper.clickGetAd();
- mTestAppHelper.verifyRenderedView();
- // Rotate to portrait layout
+ // Rotate to portrait layout and do nothing
mTestAppHelper.setOrientationPortrait();
+
+ // Rotate to landscape layout and get Ad
+ mTestAppHelper.setOrientationLandscape();
mTestAppHelper.clickGetAd();
mTestAppHelper.verifyRenderedView();
+
+ // Rotate to portrait layout and do nothing
+ mTestAppHelper.setOrientationPortrait();
+
+ // Rotate to landscape layout and do nothing
+ mTestAppHelper.setOrientationLandscape();
}
/** Return device to original state after test exeuction */
diff --git a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/TestAppHelper.java b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/TestAppHelper.java
index 6c2c720e..0b62b3e1 100644
--- a/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/TestAppHelper.java
+++ b/tests/perftests/scenarios/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/TestAppHelper.java
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertNotNull;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.util.Log;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
@@ -36,13 +37,19 @@ import java.io.IOException;
/** Helper class for interacting with OdpClient test app in perf tests. */
public class TestAppHelper {
+ private static final String TAG = TestAppHelper.class.getSimpleName();
private static final UiDevice sUiDevice = UiDevice.getInstance(getInstrumentation());
private static UiScrollable sUiScrollable;
private static final long UI_FIND_RESOURCE_TIMEOUT = 5000;
- private static final long UI_ROTATE_IDLE_TIMEOUT = 5000;
+ private static final long UI_ROTATE_IDLE_TIMEOUT = 2500;
private static final String ODP_CLIENT_TEST_APP_PACKAGE_NAME = "com.example.odpclient";
private static final String GET_AD_BUTTON_RESOURCE_ID = "get_ad_button";
private static final String RENDERED_VIEW_RESOURCE_ID = "rendered_view";
+ private static final String REPORT_CONVERSION_TEXT_BOX_RESOURCE_ID =
+ "report_conversion_text_box";
+ private static final String REPORT_CONVERSION_BUTTON_RESOURCE_ID = "report_conversion_button";
+ private static final String REPORT_CONVERSION_SUCCESS_LOG = "execute() success";
+ private static final long REPORT_CONVERSION_TIMEOUT = 10_000;
/** Commands to prepare the device and odp module before testing. */
public static void initialize() throws IOException {
@@ -81,6 +88,15 @@ public class TestAppHelper {
SystemClock.sleep(5000);
}
+ /** Kill running processes to get performance measurement under cold start */
+ public static void killRunningProcess() throws IOException {
+ executeShellCommand("am kill com.google.android.ondevicepersonalization.services");
+ executeShellCommand("am kill com.google.android.ondevicepersonalization.services:"
+ + "com.android.ondevicepersonalization."
+ + "libraries.plugin.internal.PluginExecutorService");
+ SystemClock.sleep(2000);
+ }
+
/** Commands to return device to original state */
public static void wrapUp() throws IOException {
executeShellCommand(
@@ -108,6 +124,7 @@ public class TestAppHelper {
/** Rotate screen to landscape orientation */
public void setOrientationLandscape() throws RemoteException {
+ Log.d(TAG, "Rotating screen to landscape orientation");
sUiDevice.unfreezeRotation();
sUiDevice.setOrientationLandscape();
SystemClock.sleep(UI_ROTATE_IDLE_TIMEOUT);
@@ -115,6 +132,7 @@ public class TestAppHelper {
/** Rotate screen to portrait orientation */
public void setOrientationPortrait() throws RemoteException {
+ Log.d(TAG, "Rotating screen to portrait orientation");
sUiDevice.unfreezeRotation();
sUiDevice.setOrientationPortrait();
SystemClock.sleep(UI_ROTATE_IDLE_TIMEOUT);
@@ -124,6 +142,7 @@ public class TestAppHelper {
public void clickGetAd() {
UiObject2 getAdButton = getGetAdButton();
assertNotNull("Get Ad button not found", getAdButton);
+ Log.d(TAG, "Clicking Get Ad button");
getAdButton.click();
}
@@ -135,6 +154,8 @@ public class TestAppHelper {
SystemClock.sleep(UI_FIND_RESOURCE_TIMEOUT);
if (renderedView.getChildCount() == 0) {
Assert.fail("Failed to render child surface view");
+ } else {
+ Log.d(TAG, "Verified child view is rendered");
}
}
@@ -151,6 +172,55 @@ public class TestAppHelper {
}
}
+ /** Put sourceAdId name down to report conversion */
+ public void inputSourceAdId(final String sourceAdId) {
+ UiObject2 reportConversionTextBox = getReportConversionTextBox();
+ assertNotNull("Report Conversion text box not found", reportConversionTextBox);
+ reportConversionTextBox.setText(sourceAdId);
+ }
+
+ /** Click Report Conversion button */
+ public void clickReportConversion() {
+ UiObject2 reportConversionButton = getReportConversionButton();
+ assertNotNull("Report Conversion button not found", reportConversionButton);
+ reportConversionButton.click();
+ SystemClock.sleep(3000);
+ }
+
+ /** Verify conversion is reported */
+ public void verifyConversionReported() throws IOException {
+ boolean foundReportConversionSuccessLog = findLog(
+ REPORT_CONVERSION_SUCCESS_LOG,
+ REPORT_CONVERSION_TIMEOUT,
+ 2000);
+
+ if (!foundReportConversionSuccessLog) {
+ Assert.fail(String.format(
+ "Failed to find report conversion success log within test window %d ms",
+ REPORT_CONVERSION_TIMEOUT));
+ }
+ }
+
+ /** Clean log buffer and set a high buffer limit to capture logs for test verification */
+ public void resetLogBuffer() {
+ executeShellCommand("logcat -c"); // Cleans the log buffer
+ executeShellCommand("logcat -G 32M"); // Set log buffer to 32MB
+ }
+
+ /** Attempt to find a specific log entry within the timeout window */
+ private boolean findLog(final String targetLog, long timeoutMillis,
+ long queryIntervalMillis) throws IOException {
+
+ long startTime = System.currentTimeMillis();
+ while (System.currentTimeMillis() - startTime < timeoutMillis) {
+ if (sUiDevice.executeShellCommand("logcat -d").contains(targetLog)) {
+ return true;
+ }
+ SystemClock.sleep(queryIntervalMillis);
+ }
+ return false;
+ }
+
private UiObject2 getGetAdButton() {
return sUiDevice.wait(
Until.findObject(By.res(ODP_CLIENT_TEST_APP_PACKAGE_NAME, GET_AD_BUTTON_RESOURCE_ID)),
@@ -185,6 +255,20 @@ public class TestAppHelper {
UI_FIND_RESOURCE_TIMEOUT);
}
+ private UiObject2 getReportConversionTextBox() {
+ return sUiDevice.wait(
+ Until.findObject(By.res(
+ ODP_CLIENT_TEST_APP_PACKAGE_NAME, REPORT_CONVERSION_TEXT_BOX_RESOURCE_ID)),
+ UI_FIND_RESOURCE_TIMEOUT);
+ }
+
+ private UiObject2 getReportConversionButton() {
+ return sUiDevice.wait(
+ Until.findObject(By.res(
+ ODP_CLIENT_TEST_APP_PACKAGE_NAME, REPORT_CONVERSION_BUTTON_RESOURCE_ID)),
+ UI_FIND_RESOURCE_TIMEOUT);
+ }
+
/** Get a UiScrollable instance configured for vertical scrolling */
private static UiScrollable getUiScrollable() {
if (sUiScrollable == null) {
diff --git a/tests/perftests/scenarios/tests/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/ReportConversionMicrobenchmark.java b/tests/perftests/scenarios/tests/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/ReportConversionMicrobenchmark.java
new file mode 100644
index 00000000..024436ea
--- /dev/null
+++ b/tests/perftests/scenarios/tests/src/android/ondevicepersonalization/test/scenario/ondevicepersonalization/ReportConversionMicrobenchmark.java
@@ -0,0 +1,36 @@
+/*
+ * 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.ondevicepersonalization.test.scenario.ondevicepersonalization;
+
+import android.platform.test.microbenchmark.Microbenchmark;
+import android.platform.test.rule.DropCachesRule;
+import android.platform.test.rule.KillAppsRule;
+import android.platform.test.rule.PressHomeRule;
+
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+@RunWith(Microbenchmark.class)
+public class ReportConversionMicrobenchmark extends ReportConversion {
+
+ @Rule
+ public RuleChain rules = RuleChain.outerRule(new DropCachesRule())
+ .around(new KillAppsRule("com.example.odpclient"))
+ .around(new KillAppsRule("com.android.chrome"))
+ .around(new PressHomeRule());
+}
diff --git a/tests/servicetests/src/com/android/ondevicepersonalization/services/display/DisplayHelperTest.java b/tests/servicetests/src/com/android/ondevicepersonalization/services/display/DisplayHelperTest.java
index b092c7a1..cd09ce69 100644
--- a/tests/servicetests/src/com/android/ondevicepersonalization/services/display/DisplayHelperTest.java
+++ b/tests/servicetests/src/com/android/ondevicepersonalization/services/display/DisplayHelperTest.java
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertNotNull;
import android.Manifest;
import android.adservices.ondevicepersonalization.RenderOutput;
+import android.adservices.ondevicepersonalization.RenderOutputParcel;
import android.adservices.ondevicepersonalization.RequestLogRecord;
import android.content.Context;
import android.hardware.display.DisplayManager;
@@ -72,7 +73,8 @@ public class DisplayHelperTest {
DisplayHelper displayHelper = new DisplayHelper(mContext);
RenderOutput renderContentResult = new RenderOutput.Builder()
.setContent("html").build();
- assertEquals("html", displayHelper.generateHtml(renderContentResult,
+ RenderOutputParcel resultParcel = new RenderOutputParcel(renderContentResult);
+ assertEquals("html", displayHelper.generateHtml(resultParcel,
mContext.getPackageName()));
}
@@ -95,8 +97,9 @@ public class DisplayHelperTest {
.setTemplateId("templateId")
.setTemplateParams(bundle)
.build();
+ RenderOutputParcel resultParcel = new RenderOutputParcel(renderContentResult);
String expected = "Hello odp! I am 100.";
- assertEquals(expected, displayHelper.generateHtml(renderContentResult,
+ assertEquals(expected, displayHelper.generateHtml(resultParcel,
mContext.getPackageName()));
}
diff --git a/tests/servicetests/src/com/android/ondevicepersonalization/services/request/RenderFlowTest.java b/tests/servicetests/src/com/android/ondevicepersonalization/services/request/RenderFlowTest.java
index e3b04486..61e2d373 100644
--- a/tests/servicetests/src/com/android/ondevicepersonalization/services/request/RenderFlowTest.java
+++ b/tests/servicetests/src/com/android/ondevicepersonalization/services/request/RenderFlowTest.java
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.adservices.ondevicepersonalization.Constants;
-import android.adservices.ondevicepersonalization.RenderOutput;
+import android.adservices.ondevicepersonalization.RenderOutputParcel;
import android.adservices.ondevicepersonalization.RenderingConfig;
import android.adservices.ondevicepersonalization.RequestLogRecord;
import android.adservices.ondevicepersonalization.aidl.IRequestSurfacePackageCallback;
@@ -156,7 +156,8 @@ public class RenderFlowTest {
super(mContext);
}
- @Override public String generateHtml(RenderOutput renderContentResult, String packageName) {
+ @Override public String generateHtml(
+ RenderOutputParcel renderContentResult, String packageName) {
mRenderedContent = renderContentResult.getContent();
mGenerateHtmlCalled = true;
return mRenderedContent;
diff --git a/tests/servicetests/src/com/test/TestPersonalizationHandler.java b/tests/servicetests/src/com/test/TestPersonalizationHandler.java
index bd629c76..9ea28b87 100644
--- a/tests/servicetests/src/com/test/TestPersonalizationHandler.java
+++ b/tests/servicetests/src/com/test/TestPersonalizationHandler.java
@@ -29,8 +29,8 @@ import android.adservices.ondevicepersonalization.RenderInput;
import android.adservices.ondevicepersonalization.RenderOutput;
import android.adservices.ondevicepersonalization.RenderingConfig;
import android.adservices.ondevicepersonalization.RequestLogRecord;
-import android.adservices.ondevicepersonalization.TrainingExampleInput;
-import android.adservices.ondevicepersonalization.TrainingExampleOutput;
+import android.adservices.ondevicepersonalization.TrainingExamplesInput;
+import android.adservices.ondevicepersonalization.TrainingExamplesOutput;
import android.annotation.NonNull;
import android.content.ContentValues;
import android.util.Log;
@@ -140,10 +140,10 @@ public class TestPersonalizationHandler implements IsolatedWorker {
}
@Override
- public void onTrainingExample(
- @NonNull TrainingExampleInput input,
- @NonNull Consumer<TrainingExampleOutput> consumer) {
- Log.d(TAG, "onTrainingExample() started.");
+ public void onTrainingExamples(
+ @NonNull TrainingExamplesInput input,
+ @NonNull Consumer<TrainingExamplesOutput> consumer) {
+ Log.d(TAG, "onTrainingExamples() started.");
Log.d(TAG, "Population name: " + input.getPopulationName());
Log.d(TAG, "Task name: " + input.getTaskName());
@@ -154,8 +154,8 @@ public class TestPersonalizationHandler implements IsolatedWorker {
tokens.add("token1".getBytes());
tokens.add("token2".getBytes());
- TrainingExampleOutput output =
- new TrainingExampleOutput.Builder()
+ TrainingExamplesOutput output =
+ new TrainingExamplesOutput.Builder()
.setTrainingExamples(examples)
.setResumptionTokens(tokens)
.build();