summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt7
-rw-r--r--keystore/java/android/security/KeyStoreSecurityLevel.java7
-rw-r--r--keystore/java/android/security/keystore/BackendBusyException.java37
3 files changed, 41 insertions, 10 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index b0534a141520..9f358f2df4a1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -36183,9 +36183,10 @@ package android.security.identity {
package android.security.keystore {
public class BackendBusyException extends java.security.ProviderException {
- ctor public BackendBusyException();
- ctor public BackendBusyException(@NonNull String);
- ctor public BackendBusyException(@NonNull String, @NonNull Throwable);
+ ctor public BackendBusyException(long);
+ ctor public BackendBusyException(long, @NonNull String);
+ ctor public BackendBusyException(long, @NonNull String, @NonNull Throwable);
+ method public long getBackOffHintMillis();
}
public class KeyExpiredException extends java.security.InvalidKeyException {
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 3ef4aa5b7ec3..56dd7f049828 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -96,20 +96,21 @@ public class KeyStoreSecurityLevel {
} catch (ServiceSpecificException e) {
switch (e.errorCode) {
case ResponseCode.BACKEND_BUSY: {
+ long backOffHint = (long) (Math.random() * 80 + 20);
if (CompatChanges.isChangeEnabled(
KeyStore2.KEYSTORE_OPERATION_CREATION_MAY_FAIL)) {
// Starting with Android S we inform the caller about the
// backend being busy.
- throw new BackendBusyException();
+ throw new BackendBusyException(backOffHint);
} else {
// Before Android S operation creation must always succeed. So we
// just have to retry. We do so with a randomized back-off between
- // 50 and 250ms.
+ // 20 and 100ms.
// It is a little awkward that we cannot break out of this loop
// by interrupting this thread. But that is the expected behavior.
// There is some comfort in the fact that interrupting a thread
// also does not unblock a thread waiting for a binder transaction.
- interruptedPreservingSleep((long) (Math.random() * 200 + 50));
+ interruptedPreservingSleep(backOffHint);
}
break;
}
diff --git a/keystore/java/android/security/keystore/BackendBusyException.java b/keystore/java/android/security/keystore/BackendBusyException.java
index 1a88469d7e54..a813e939a720 100644
--- a/keystore/java/android/security/keystore/BackendBusyException.java
+++ b/keystore/java/android/security/keystore/BackendBusyException.java
@@ -16,37 +16,66 @@
package android.security.keystore;
+import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import java.security.ProviderException;
/**
* Indicates a transient error that prevented a key operation from being created.
- * Callers should try again with a back-off period of 10-30 milliseconds.
+ * Callers should try again with a back-off period of {@link #getBackOffHintMillis()}
+ * milliseconds.
*/
public class BackendBusyException extends ProviderException {
+ private final long mBackOffHintMillis;
+
/**
* Constructs a new {@code BackendBusyException} without detail message and cause.
+ *
*/
- public BackendBusyException() {
+ public BackendBusyException(@DurationMillisLong long backOffHintMillis) {
super("The keystore backend has no operation slots available. Retry later.");
+ if (backOffHintMillis < 0) {
+ throw new IllegalArgumentException("Back-off hint cannot be negative.");
+ }
+ mBackOffHintMillis = backOffHintMillis;
}
/**
* Constructs a new {@code BackendBusyException} with the provided detail message and
* no cause.
*/
- public BackendBusyException(@NonNull String message) {
+ public BackendBusyException(@DurationMillisLong long backOffHintMillis,
+ @NonNull String message) {
super(message);
+ if (backOffHintMillis < 0) {
+ throw new IllegalArgumentException("Back-off hint cannot be negative.");
+ }
+ mBackOffHintMillis = backOffHintMillis;
}
/**
* Constructs a new {@code BackendBusyException} with the provided detail message and
* cause.
*/
- public BackendBusyException(@NonNull String message, @NonNull Throwable cause) {
+ public BackendBusyException(@DurationMillisLong long backOffHintMillis,
+ @NonNull String message, @NonNull Throwable cause) {
super(message, cause);
+ if (backOffHintMillis < 0) {
+ throw new IllegalArgumentException("Back-off hint cannot be negative.");
+ }
+ mBackOffHintMillis = backOffHintMillis;
}
+ /**
+ * When retrying to start a Keystore operation after receiving this exception, this can be
+ * used to determine how long to wait before retrying. It is not guaranteed that the operation
+ * will succeeds after this time. Multiple retries may be necessary if the system is congested.
+ *
+ * @return Number of milliseconds to back off before retrying.
+ */
+ public @DurationMillisLong long getBackOffHintMillis() {
+ return mBackOffHintMillis;
+ }
}