Loading core/api/current.txt +4 −3 Original line number Diff line number Diff line Loading @@ -36185,9 +36185,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 { keystore/java/android/security/KeyStoreSecurityLevel.java +4 −3 Original line number Diff line number Diff line Loading @@ -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; } Loading keystore/java/android/security/keystore/BackendBusyException.java +33 −4 Original line number Diff line number Diff line Loading @@ -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; } } Loading
core/api/current.txt +4 −3 Original line number Diff line number Diff line Loading @@ -36185,9 +36185,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 {
keystore/java/android/security/KeyStoreSecurityLevel.java +4 −3 Original line number Diff line number Diff line Loading @@ -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; } Loading
keystore/java/android/security/keystore/BackendBusyException.java +33 −4 Original line number Diff line number Diff line Loading @@ -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; } }