1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.bedstead.remoteframeworkclasses;
18 
19 import android.accounts.AccountManagerFuture;
20 import android.accounts.AuthenticatorException;
21 import android.accounts.OperationCanceledException;
22 
23 import java.io.IOException;
24 import java.util.concurrent.CountDownLatch;
25 import java.util.concurrent.TimeUnit;
26 import java.util.concurrent.atomic.AtomicReference;
27 
28 /**
29  * A simple implementation of {@link AccountManagerFuture} for use with remote calls.
30  *
31  * @param <V> The wrapped future result.
32  */
33 public final class SimpleAccountManagerFuture<V> implements AccountManagerFuture<V> {
34 
35     private final CountDownLatch mIsDone = new CountDownLatch(1);
36     private final AtomicReference<Throwable> mException = new AtomicReference<>();
37     private final AtomicReference<V> mValue = new AtomicReference<>();
38 
39     @Override
cancel(boolean mayInterruptIfRunning)40     public boolean cancel(boolean mayInterruptIfRunning) {
41         // Remote calls cannot cancel futures
42         return false;
43     }
44 
45     @Override
isCancelled()46     public boolean isCancelled() {
47         return false;
48     }
49 
50     @Override
isDone()51     public boolean isDone() {
52         return mIsDone.getCount() < 1;
53     }
54 
55     @Override
getResult()56     public V getResult() throws OperationCanceledException, IOException, AuthenticatorException {
57         try {
58             mIsDone.await();
59         } catch (InterruptedException e) {
60             throw new RuntimeException("Interrupted while waiting for result", e);
61         }
62 
63         Throwable exception = mException.get();
64         if (exception == null) {
65             return mValue.get();
66         } else if (exception instanceof OperationCanceledException) {
67             throw (OperationCanceledException) exception;
68         } else if (exception instanceof IOException) {
69             throw (IOException)  exception;
70         } else if (exception instanceof AuthenticatorException) {
71             throw (AuthenticatorException) exception;
72         } else {
73             throw new RuntimeException("Unexpected exception type", exception);
74         }
75     }
76 
77     /** Set the result of this future. */
setResult(V result)78     public void setResult(V result) {
79         mValue.set(result);
80         mIsDone.countDown();
81     }
82 
83     @Override
getResult(long timeout, TimeUnit unit)84     public V getResult(long timeout, TimeUnit unit)
85             throws OperationCanceledException, IOException, AuthenticatorException {
86         try {
87             mIsDone.await(timeout, unit);
88         } catch (InterruptedException e) {
89             throw new RuntimeException("Interrupted while waiting for result", e);
90         }
91 
92         Throwable exception = mException.get();
93         if (exception == null) {
94             return mValue.get();
95         } else if (exception instanceof OperationCanceledException) {
96             throw (OperationCanceledException) exception;
97         } else if (exception instanceof IOException) {
98             throw (IOException)  exception;
99         } else if (exception instanceof AuthenticatorException) {
100             throw (AuthenticatorException) exception;
101         } else {
102             throw new RuntimeException("Unexpected exception type", exception);
103         }
104     }
105 
106     /** Set an exception thrown by this future. */
setException(Throwable throwable)107     public void setException(Throwable throwable) {
108         mException.set(throwable);
109         mIsDone.countDown();
110     }
111 }
112