Preserve interrupted state across waiting for future

A normal synchronous binder call would not be influenced by an
interrupted thread. With the move to asynchronous keystore IPC we wait
on a future which can throw an interrupted exception. The Java crypto
API does not expect the implementation to throw interrupted exceptions
though. So to preserve the expected behavior we wrap the Future.get()
calls in a loop that handles the interrupted exception and sets the
interrupted state after the get completed successfully.

Bug: 147398412
Bug: 155254932
Test: atest android.keystore.cts.CipherTest#testEncryptsAndDecryptsInterrupted
Change-Id: I066180e8028cc426fa1b3739fa007faa17c8c012
(cherry picked from commit 022c8ef71354aa059f8377acfe31e64880784da0)
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 9d0fe11..88b614d 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -483,14 +483,14 @@
             mBinder.asBinder().linkToDeath(promise, 0);
             int errorCode = mBinder.addRngEntropy(promise, data, flags);
             if (errorCode == NO_ERROR) {
-                return promise.getFuture().get().getErrorCode() == NO_ERROR;
+                return interruptedPreservingGet(promise.getFuture()).getErrorCode() == NO_ERROR;
             } else {
                 return false;
             }
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return false;
-        } catch (ExecutionException | InterruptedException e) {
+        } catch (ExecutionException e) {
             Log.e(TAG, "AddRngEntropy completed with exception", e);
             return false;
         } finally {
@@ -548,7 +548,7 @@
 
     private int generateKeyInternal(String alias, KeymasterArguments args, byte[] entropy, int uid,
             int flags, KeyCharacteristics outCharacteristics)
-                    throws RemoteException, ExecutionException, InterruptedException {
+                    throws RemoteException, ExecutionException {
         KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
         int error = NO_ERROR;
         KeyCharacteristicsCallbackResult result = null;
@@ -559,7 +559,7 @@
                 Log.e(TAG, "generateKeyInternal failed on request " + error);
                 return error;
             }
-            result = promise.getFuture().get();
+            result = interruptedPreservingGet(promise.getFuture());
         } finally {
             mBinder.asBinder().unlinkToDeath(promise, 0);
         }
@@ -592,7 +592,7 @@
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
-        } catch (ExecutionException | InterruptedException e) {
+        } catch (ExecutionException e) {
             Log.e(TAG, "generateKey completed with exception", e);
             return SYSTEM_ERROR;
         }
@@ -614,7 +614,7 @@
             int error = mBinder.getKeyCharacteristics(promise, alias, clientId, appId, uid);
             if (error != NO_ERROR) return error;
 
-            KeyCharacteristicsCallbackResult result = promise.getFuture().get();
+            KeyCharacteristicsCallbackResult result = interruptedPreservingGet(promise.getFuture());
             error = result.getKeystoreResponse().getErrorCode();
             if (error != NO_ERROR) return error;
 
@@ -625,7 +625,7 @@
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
-        } catch (ExecutionException | InterruptedException e) {
+        } catch (ExecutionException e) {
             Log.e(TAG, "GetKeyCharacteristics completed with exception", e);
             return SYSTEM_ERROR;
         } finally {
@@ -640,14 +640,14 @@
 
     private int importKeyInternal(String alias, KeymasterArguments args, int format, byte[] keyData,
             int uid, int flags, KeyCharacteristics outCharacteristics)
-                    throws RemoteException, ExecutionException, InterruptedException {
+                    throws RemoteException, ExecutionException {
         KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
         mBinder.asBinder().linkToDeath(promise, 0);
         try {
             int error = mBinder.importKey(promise, alias, args, format, keyData, uid, flags);
             if (error != NO_ERROR) return error;
 
-            KeyCharacteristicsCallbackResult result = promise.getFuture().get();
+            KeyCharacteristicsCallbackResult result = interruptedPreservingGet(promise.getFuture());
 
             error = result.getKeystoreResponse().getErrorCode();
             if (error != NO_ERROR) return error;
@@ -675,7 +675,7 @@
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
-        } catch (ExecutionException | InterruptedException e) {
+        } catch (ExecutionException e) {
             Log.e(TAG, "ImportKey completed with exception", e);
             return SYSTEM_ERROR;
         }
@@ -747,7 +747,7 @@
             String wrappingKeyAlias,
             byte[] maskingKey, KeymasterArguments args, long rootSid, long fingerprintSid,
             KeyCharacteristics outCharacteristics)
-                    throws RemoteException, ExecutionException, InterruptedException {
+                    throws RemoteException, ExecutionException {
         KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
         mBinder.asBinder().linkToDeath(promise, 0);
         try {
@@ -755,7 +755,7 @@
                     wrappingKeyAlias, maskingKey, args, rootSid, fingerprintSid);
             if (error != NO_ERROR) return error;
 
-            KeyCharacteristicsCallbackResult result = promise.getFuture().get();
+            KeyCharacteristicsCallbackResult result = interruptedPreservingGet(promise.getFuture());
 
             error = result.getKeystoreResponse().getErrorCode();
             if (error != NO_ERROR) return error;
@@ -786,7 +786,7 @@
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
-        } catch (ExecutionException | InterruptedException e) {
+        } catch (ExecutionException e) {
             Log.e(TAG, "ImportWrappedKey completed with exception", e);
             return SYSTEM_ERROR;
         }
@@ -818,14 +818,14 @@
             appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
             int error = mBinder.exportKey(promise, alias, format, clientId, appId, uid);
             if (error == NO_ERROR) {
-                return promise.getFuture().get();
+                return interruptedPreservingGet(promise.getFuture());
             } else {
                 return new ExportResult(error);
             }
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
-        } catch (ExecutionException | InterruptedException e) {
+        } catch (ExecutionException e) {
             Log.e(TAG, "ExportKey completed with exception", e);
             return null;
         } finally {
@@ -864,14 +864,14 @@
             int errorCode =  mBinder.begin(promise, getToken(), alias, purpose, pruneable, args,
                                            entropy, uid);
             if (errorCode == NO_ERROR) {
-                return promise.getFuture().get();
+                return interruptedPreservingGet(promise.getFuture());
             } else {
                 return new OperationResult(errorCode);
             }
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
-        } catch (ExecutionException | InterruptedException e) {
+        } catch (ExecutionException e) {
             Log.e(TAG, "Begin completed with exception", e);
             return null;
         } finally {
@@ -894,14 +894,14 @@
             input = input != null ? input : new byte[0];
             int errorCode =  mBinder.update(promise, token, arguments, input);
             if (errorCode == NO_ERROR) {
-                return promise.getFuture().get();
+                return interruptedPreservingGet(promise.getFuture());
             } else {
                 return new OperationResult(errorCode);
             }
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
-        } catch (ExecutionException | InterruptedException e) {
+        } catch (ExecutionException e) {
             Log.e(TAG, "Update completed with exception", e);
             return null;
         } finally {
@@ -930,14 +930,14 @@
             signature = signature != null ? signature : new byte[0];
             int errorCode = mBinder.finish(promise, token, arguments, input, signature, entropy);
             if (errorCode == NO_ERROR) {
-                return promise.getFuture().get();
+                return interruptedPreservingGet(promise.getFuture());
             } else {
                 return new OperationResult(errorCode);
             }
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
-        } catch (ExecutionException | InterruptedException e) {
+        } catch (ExecutionException e) {
             Log.e(TAG, "Finish completed with exception", e);
             return null;
         } finally {
@@ -972,14 +972,14 @@
             mBinder.asBinder().linkToDeath(promise, 0);
             int errorCode = mBinder.abort(promise, token);
             if (errorCode == NO_ERROR) {
-                return promise.getFuture().get().getErrorCode();
+                return interruptedPreservingGet(promise.getFuture()).getErrorCode();
             } else {
                 return errorCode;
             }
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
-        } catch (ExecutionException | InterruptedException e) {
+        } catch (ExecutionException e) {
             Log.e(TAG, "Abort completed with exception", e);
             return SYSTEM_ERROR;
         } finally {
@@ -1135,7 +1135,7 @@
             }
             int error = mBinder.attestKey(promise, alias, params);
             if (error != NO_ERROR) return error;
-            KeyAttestationCallbackResult result = promise.getFuture().get();
+            KeyAttestationCallbackResult result = interruptedPreservingGet(promise.getFuture());
             error = result.getKeystoreResponse().getErrorCode();
             if (error == NO_ERROR) {
                 outChain.shallowCopyFrom(result.getCertificateChain());
@@ -1144,7 +1144,7 @@
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
-        } catch (ExecutionException | InterruptedException e) {
+        } catch (ExecutionException e) {
             Log.e(TAG, "AttestKey completed with exception", e);
             return SYSTEM_ERROR;
         } finally {
@@ -1164,7 +1164,7 @@
             }
             int error = mBinder.attestDeviceIds(promise, params);
             if (error != NO_ERROR) return error;
-            KeyAttestationCallbackResult result = promise.getFuture().get();
+            KeyAttestationCallbackResult result = interruptedPreservingGet(promise.getFuture());
             error = result.getKeystoreResponse().getErrorCode();
             if (error == NO_ERROR) {
                 outChain.shallowCopyFrom(result.getCertificateChain());
@@ -1173,7 +1173,7 @@
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
-        } catch (ExecutionException | InterruptedException e) {
+        } catch (ExecutionException e) {
             Log.e(TAG, "AttestDevicdeIds completed with exception", e);
             return SYSTEM_ERROR;
         } finally {
@@ -1387,4 +1387,20 @@
             int errorCode) {
         return getInvalidKeyException(keystoreKeyAlias, uid, getKeyStoreException(errorCode));
     }
+
+    private static <R> R interruptedPreservingGet(CompletableFuture<R> future)
+            throws ExecutionException {
+        boolean wasInterrupted = false;
+        while (true) {
+            try {
+                R result = future.get();
+                if (wasInterrupted) {
+                    Thread.currentThread().interrupt();
+                }
+                return result;
+            } catch (InterruptedException e) {
+                wasInterrupted = true;
+            }
+        }
+    }
 }