Add timeout to getAdServicesCommonState IPC call
Bug: 362771868
Test: atest UserPrivacyStatusTest PhFlagsTest -c, manual test the flows
Flag: EXEMPT (bug 337358613)
Change-Id: I5b17153acdc745952cf1e77763e545e76d8f003b
diff --git a/src/com/android/ondevicepersonalization/services/Flags.java b/src/com/android/ondevicepersonalization/services/Flags.java
index 2a4bc90..6543834 100644
--- a/src/com/android/ondevicepersonalization/services/Flags.java
+++ b/src/com/android/ondevicepersonalization/services/Flags.java
@@ -337,4 +337,13 @@
default int getMaxIntValuesLimit() {
return DEFAULT_MAX_INT_VALUES;
}
+
+ /**
+ * Default max wait time until timeout for AdServices IPC call
+ */
+ long DEFAULT_ADSERVICES_IPC_CALL_TIMEOUT_IN_MILLIS = 5000L;
+
+ default long getAdservicesIpcCallTimeoutInMillis() {
+ return DEFAULT_ADSERVICES_IPC_CALL_TIMEOUT_IN_MILLIS;
+ }
}
diff --git a/src/com/android/ondevicepersonalization/services/PhFlags.java b/src/com/android/ondevicepersonalization/services/PhFlags.java
index 73143a3..1111b0a 100644
--- a/src/com/android/ondevicepersonalization/services/PhFlags.java
+++ b/src/com/android/ondevicepersonalization/services/PhFlags.java
@@ -113,6 +113,9 @@
public static final String MAX_INT_VALUES_LIMIT = "max_int_values_limit";
+ public static final String KEY_ADSERVICES_IPC_CALL_TIMEOUT_IN_MILLIS =
+ "adservices_ipc_call_timeout_in_millis";
+
// OnDevicePersonalization Namespace String from DeviceConfig class
public static final String NAMESPACE_ON_DEVICE_PERSONALIZATION = "on_device_personalization";
@@ -470,4 +473,12 @@
/* name= */ MAX_INT_VALUES_LIMIT,
/* defaultValue= */ DEFAULT_MAX_INT_VALUES);
}
+
+ @Override
+ public long getAdservicesIpcCallTimeoutInMillis() {
+ return DeviceConfig.getLong(
+ /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
+ /* name= */ KEY_ADSERVICES_IPC_CALL_TIMEOUT_IN_MILLIS,
+ /* defaultValue= */ DEFAULT_ADSERVICES_IPC_CALL_TIMEOUT_IN_MILLIS);
+ }
}
diff --git a/src/com/android/ondevicepersonalization/services/data/user/AdServicesCommonStatesWrapperImpl.java b/src/com/android/ondevicepersonalization/services/data/user/AdServicesCommonStatesWrapperImpl.java
index fa0eef4..d08c8ab 100644
--- a/src/com/android/ondevicepersonalization/services/data/user/AdServicesCommonStatesWrapperImpl.java
+++ b/src/com/android/ondevicepersonalization/services/data/user/AdServicesCommonStatesWrapperImpl.java
@@ -22,9 +22,12 @@
import android.adservices.common.AdServicesOutcomeReceiver;
import android.annotation.NonNull;
import android.content.Context;
+import android.os.Binder;
import androidx.concurrent.futures.CallbackToFutureAdapter;
+import com.android.ondevicepersonalization.internal.util.LoggerFactory;
+import com.android.ondevicepersonalization.services.FlagsFactory;
import com.android.ondevicepersonalization.services.OnDevicePersonalizationExecutors;
import com.google.common.util.concurrent.FluentFuture;
@@ -33,12 +36,15 @@
import com.google.common.util.concurrent.MoreExecutors;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
/**
* A wrapper for the AdServicesCommonStates API. Used by UserPrivacyStatus to
* fetch common states from AdServices.
*/
class AdServicesCommonStatesWrapperImpl implements AdServicesCommonStatesWrapper {
+ private static final String TAG = AdServicesCommonStatesWrapperImpl.class.getSimpleName();
+ private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
private final Context mContext;
AdServicesCommonStatesWrapperImpl(Context context) {
@@ -49,7 +55,18 @@
try {
AdServicesCommonManager manager =
Objects.requireNonNull(getAdServicesCommonManager());
- return FluentFuture.from(getAdServicesResponse(manager))
+ sLogger.d(TAG + ": IPC getAdServicesCommonStates() started");
+ long origId = Binder.clearCallingIdentity();
+ long timeoutInMillis = FlagsFactory.getFlags().getAdservicesIpcCallTimeoutInMillis();
+ Binder.restoreCallingIdentity(origId);
+ ListenableFuture<AdServicesCommonStatesResponse> futureWithTimeout =
+ Futures.withTimeout(
+ getAdServicesResponse(manager),
+ timeoutInMillis,
+ TimeUnit.MILLISECONDS,
+ OnDevicePersonalizationExecutors.getScheduledExecutor());
+
+ return FluentFuture.from(futureWithTimeout)
.transform(
v -> getResultFromResponse(v),
MoreExecutors.newDirectExecutorService());
@@ -83,11 +100,15 @@
Exception>() {
@Override
public void onResult(AdServicesCommonStatesResponse result) {
+ sLogger.d(
+ TAG + ": IPC getAdServicesCommonStates() success");
completer.set(result);
}
@Override
public void onError(Exception error) {
+ sLogger.e(error,
+ TAG + ": IPC getAdServicesCommonStates() error");
completer.setException(error);
}
});
diff --git a/src/com/android/ondevicepersonalization/services/data/user/UserPrivacyStatus.java b/src/com/android/ondevicepersonalization/services/data/user/UserPrivacyStatus.java
index c4b91f0..15b698d 100644
--- a/src/com/android/ondevicepersonalization/services/data/user/UserPrivacyStatus.java
+++ b/src/com/android/ondevicepersonalization/services/data/user/UserPrivacyStatus.java
@@ -22,6 +22,7 @@
import static android.adservices.ondevicepersonalization.Constants.STATUS_METHOD_NOT_FOUND;
import static android.adservices.ondevicepersonalization.Constants.STATUS_REMOTE_EXCEPTION;
import static android.adservices.ondevicepersonalization.Constants.STATUS_SUCCESS;
+import static android.adservices.ondevicepersonalization.Constants.STATUS_TIMEOUT;
import static com.android.ondevicepersonalization.services.PhFlags.KEY_ENABLE_PERSONALIZATION_STATUS_OVERRIDE;
import static com.android.ondevicepersonalization.services.PhFlags.KEY_PERSONALIZATION_STATUS_OVERRIDE_VALUE;
@@ -39,6 +40,8 @@
import com.android.ondevicepersonalization.services.util.StatsUtils;
import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
/**
* A singleton class that stores all user privacy statuses in memory.
@@ -242,8 +245,8 @@
int updatedMeasurementState = commonStates.getMeasurementState();
updateUserControlCache(updatedProtectedAudienceState, updatedMeasurementState);
} catch (Exception e) {
- sLogger.e(TAG + ": fetchStateFromAdServices error", e);
int statusCode = getExceptionStatus(e);
+ sLogger.e(e, TAG + ": fetchStateFromAdServices error, status code %d", statusCode);
StatsUtils.writeServiceRequestMetrics(
API_NAME_ADSERVICES_GET_COMMON_STATES,
packageName,
@@ -262,6 +265,9 @@
@VisibleForTesting
int getExceptionStatus(Exception e) {
+ if (e instanceof ExecutionException && e.getCause() instanceof TimeoutException) {
+ return STATUS_TIMEOUT;
+ }
if (e instanceof NoSuchMethodException) {
return STATUS_METHOD_NOT_FOUND;
}
diff --git a/tests/servicetests/src/com/android/ondevicepersonalization/services/PhFlagsTest.java b/tests/servicetests/src/com/android/ondevicepersonalization/services/PhFlagsTest.java
index d967044..49f3c48 100644
--- a/tests/servicetests/src/com/android/ondevicepersonalization/services/PhFlagsTest.java
+++ b/tests/servicetests/src/com/android/ondevicepersonalization/services/PhFlagsTest.java
@@ -19,6 +19,7 @@
import static com.android.adservices.shared.common.flags.ModuleSharedFlags.BACKGROUND_JOB_SAMPLING_LOGGING_RATE;
import static com.android.adservices.shared.common.flags.ModuleSharedFlags.DEFAULT_JOB_SCHEDULING_LOGGING_SAMPLING_RATE;
import static com.android.ondevicepersonalization.services.Flags.APP_REQUEST_FLOW_DEADLINE_SECONDS;
+import static com.android.ondevicepersonalization.services.Flags.DEFAULT_ADSERVICES_IPC_CALL_TIMEOUT_IN_MILLIS;
import static com.android.ondevicepersonalization.services.Flags.DEFAULT_AGGREGATED_ERROR_REPORTING_ENABLED;
import static com.android.ondevicepersonalization.services.Flags.DEFAULT_AGGREGATED_ERROR_REPORTING_INTERVAL_HOURS;
import static com.android.ondevicepersonalization.services.Flags.DEFAULT_AGGREGATED_ERROR_REPORTING_THRESHOLD;
@@ -43,6 +44,7 @@
import static com.android.ondevicepersonalization.services.Flags.WEB_TRIGGER_FLOW_DEADLINE_SECONDS;
import static com.android.ondevicepersonalization.services.Flags.WEB_VIEW_FLOW_DEADLINE_SECONDS;
import static com.android.ondevicepersonalization.services.PhFlags.APP_INSTALL_HISTORY_TTL;
+import static com.android.ondevicepersonalization.services.PhFlags.KEY_ADSERVICES_IPC_CALL_TIMEOUT_IN_MILLIS;
import static com.android.ondevicepersonalization.services.PhFlags.KEY_AGGREGATED_ERROR_REPORTING_INTERVAL_HOURS;
import static com.android.ondevicepersonalization.services.PhFlags.KEY_AGGREGATED_ERROR_REPORTING_PATH;
import static com.android.ondevicepersonalization.services.PhFlags.KEY_AGGREGATED_ERROR_REPORTING_THRESHOLD;
@@ -731,4 +733,26 @@
assertThat(FlagsFactory.getFlags().getAggregatedErrorReportingIntervalInHours())
.isEqualTo(DEFAULT_AGGREGATED_ERROR_REPORTING_INTERVAL_HOURS);
}
+
+ @Test
+ public void testGetAdservicesIpcCallTimeoutInMillis() {
+ long testTimeoutValue = 100L;
+
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+ KEY_ADSERVICES_IPC_CALL_TIMEOUT_IN_MILLIS,
+ Long.toString(testTimeoutValue),
+ /* makeDefault */ false);
+
+ assertThat(FlagsFactory.getFlags().getAdservicesIpcCallTimeoutInMillis())
+ .isEqualTo(testTimeoutValue);
+
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_ON_DEVICE_PERSONALIZATION,
+ KEY_ADSERVICES_IPC_CALL_TIMEOUT_IN_MILLIS,
+ Long.toString(DEFAULT_ADSERVICES_IPC_CALL_TIMEOUT_IN_MILLIS),
+ /* makeDefault */ false);
+ assertThat(FlagsFactory.getFlags().getAdservicesIpcCallTimeoutInMillis())
+ .isEqualTo(DEFAULT_ADSERVICES_IPC_CALL_TIMEOUT_IN_MILLIS);
+ }
}
diff --git a/tests/servicetests/src/com/android/ondevicepersonalization/services/data/user/UserPrivacyStatusTest.java b/tests/servicetests/src/com/android/ondevicepersonalization/services/data/user/UserPrivacyStatusTest.java
index abc91b7..8e8edeb 100644
--- a/tests/servicetests/src/com/android/ondevicepersonalization/services/data/user/UserPrivacyStatusTest.java
+++ b/tests/servicetests/src/com/android/ondevicepersonalization/services/data/user/UserPrivacyStatusTest.java
@@ -20,6 +20,7 @@
import static android.adservices.ondevicepersonalization.Constants.STATUS_INTERNAL_ERROR;
import static android.adservices.ondevicepersonalization.Constants.STATUS_METHOD_NOT_FOUND;
import static android.adservices.ondevicepersonalization.Constants.STATUS_REMOTE_EXCEPTION;
+import static android.adservices.ondevicepersonalization.Constants.STATUS_TIMEOUT;
import static android.app.job.JobScheduler.RESULT_SUCCESS;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -57,6 +58,9 @@
import org.mockito.Spy;
import org.mockito.quality.Strictness;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
@RunWith(JUnit4.class)
public final class UserPrivacyStatusTest {
private UserPrivacyStatus mUserPrivacyStatus;
@@ -165,6 +169,9 @@
@Test
public void testGetStatusCode() {
+ assertThat(mUserPrivacyStatus.getExceptionStatus(
+ new ExecutionException("timeout testing", new TimeoutException())))
+ .isEqualTo(STATUS_TIMEOUT);
assertThat(mUserPrivacyStatus.getExceptionStatus(new NoSuchMethodException()))
.isEqualTo(STATUS_METHOD_NOT_FOUND);
assertThat(mUserPrivacyStatus.getExceptionStatus(new SecurityException()))