• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 package com.android.modules.utils;
17 
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.Handler;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.os.RemoteException;
25 import android.os.SystemClock;
26 import android.util.Log;
27 
28 import com.android.internal.annotations.GuardedBy;
29 
30 import java.io.Serializable;
31 import java.time.Duration;
32 import java.util.List;
33 import java.util.Objects;
34 import java.util.concurrent.CompletableFuture;
35 import java.util.concurrent.ConcurrentLinkedQueue;
36 import java.util.concurrent.ExecutionException;
37 import java.util.concurrent.TimeUnit;
38 import java.util.concurrent.TimeoutException;
39 
40 /**
41  * Generic interface for receiving a callback result from someone.
42  * Allow the server end to synchronously wait on the response from the client.
43  * This enables an RPC like system but with the ability to timeout and discard late results.
44  *
45  * <p>NOTE: Use the static {@link #get} method to retrieve an available instance of this class.
46  * If no instances are available, a new one is created.
47  */
48 public final class SynchronousResultReceiver<T> implements Parcelable {
49     private static final String TAG = "SynchronousResultReceiver";
50     private final boolean mLocal;
51     private boolean mIsCompleted;
52     private final static Object sLock = new Object();
53     private final static int QUEUE_THRESHOLD = 4;
54 
55     @GuardedBy("sLock")
56     private CompletableFuture<Result<T>> mFuture = new CompletableFuture<>();
57 
58     @GuardedBy("sLock")
59     private static final ConcurrentLinkedQueue<SynchronousResultReceiver> sAvailableReceivers
60             = new ConcurrentLinkedQueue<>();
61 
get()62     public static <T> SynchronousResultReceiver<T> get() {
63         synchronized(sLock) {
64             if (sAvailableReceivers.isEmpty()) {
65                 return new SynchronousResultReceiver();
66             }
67             SynchronousResultReceiver receiver = sAvailableReceivers.poll();
68             receiver.resetLocked();
69             return receiver;
70         }
71     }
72 
SynchronousResultReceiver()73     private SynchronousResultReceiver() {
74         mLocal = true;
75         mIsCompleted = false;
76     }
77 
78     @GuardedBy("sLock")
releaseLocked()79     private void releaseLocked() {
80         mFuture = null;
81         if (sAvailableReceivers.size() < QUEUE_THRESHOLD) {
82             sAvailableReceivers.add(this);
83         }
84     }
85 
86     @GuardedBy("sLock")
resetLocked()87     private void resetLocked() {
88         mFuture = new CompletableFuture<>();
89         mIsCompleted = false;
90     }
91 
getFuture()92     private CompletableFuture<Result<T>> getFuture() {
93        synchronized (sLock) {
94            return mFuture;
95        }
96     }
97 
98     public static class Result<T> implements Parcelable {
99         private final @Nullable T mObject;
100         private final RuntimeException mException;
101 
Result(RuntimeException exception)102         public Result(RuntimeException exception) {
103             mObject = null;
104             mException = exception;
105         }
106 
Result(@ullable T object)107         public Result(@Nullable T object) {
108             mObject = object;
109             mException = null;
110         }
111 
112         /**
113          * Return the stored value
114          * May throw a {@link RuntimeException} thrown from the client
115          */
getValue(T defaultValue)116         public T getValue(T defaultValue) {
117             if (mException != null) {
118                 throw mException;
119             }
120             if (mObject == null) {
121                 return defaultValue;
122             }
123             return mObject;
124         }
125 
describeContents()126         public int describeContents() {
127             return 0;
128         }
129 
writeToParcel(@onNull Parcel out, int flags)130         public void writeToParcel(@NonNull Parcel out, int flags) {
131             out.writeValue(mObject);
132             out.writeValue(mException);
133         }
134 
Result(Parcel in)135         private Result(Parcel in) {
136             mObject = (T)in.readValue(null);
137             mException= (RuntimeException)in.readValue(null);
138         }
139 
140         public static final @NonNull Parcelable.Creator<Result<?>> CREATOR =
141             new Parcelable.Creator<Result<?>>() {
142                 public Result createFromParcel(Parcel in) {
143                     return new Result(in);
144                 }
145                 public Result[] newArray(int size) {
146                     return new Result[size];
147                 }
148             };
149     }
150 
complete(Result<T> result)151     private void complete(Result<T> result) {
152         if (mIsCompleted) {
153             throw new IllegalStateException("Receiver has already been completed");
154         }
155         mIsCompleted = true;
156         if (mLocal) {
157             getFuture().complete(result);
158         } else {
159             final ISynchronousResultReceiver rr;
160             synchronized (this) {
161                 rr = mReceiver;
162             }
163             if (rr != null) {
164                 try {
165                     rr.send(result);
166                 } catch (RemoteException e) {
167                     Log.w(TAG, "Failed to complete future");
168                 }
169             }
170         }
171     }
172 
173     /**
174      * Deliver a result to this receiver.
175      *
176      * @param resultData Additional data provided by you.
177      */
send(@ullable T resultData)178     public void send(@Nullable T resultData) {
179         complete(new Result<>(resultData));
180     }
181 
182     /**
183      * Deliver an {@link Exception} to this receiver
184      *
185      * @param e exception to be sent
186      */
propagateException(@onNull RuntimeException e)187     public void propagateException(@NonNull RuntimeException e) {
188         Objects.requireNonNull(e, "RuntimeException cannot be null");
189         complete(new Result<>(e));
190     }
191 
192     /**
193      * Blocks waiting for the result from the remote client.
194      *
195      * If it is interrupted before completion of the duration, wait again with remaining time until
196      * the deadline.
197      *
198      * @param timeout The duration to wait before sending a {@link TimeoutException}
199      * @return the Result
200      * @throws TimeoutException if the timeout in milliseconds expired.
201      */
awaitResultNoInterrupt(@onNull Duration timeout)202     public @NonNull Result<T> awaitResultNoInterrupt(@NonNull Duration timeout)
203             throws TimeoutException {
204         Objects.requireNonNull(timeout, "Null timeout is not allowed");
205 
206         final long startWaitNanoTime = SystemClock.elapsedRealtimeNanos();
207         Duration remainingTime = timeout;
208         while (!remainingTime.isNegative()) {
209             try {
210                 Result<T> result = getFuture().get(remainingTime.toMillis(), TimeUnit.MILLISECONDS);
211                 synchronized (sLock) {
212                     releaseLocked();
213                     return result;
214                 }
215             } catch (ExecutionException e) {
216                 // This will NEVER happen.
217                 throw new AssertionError("Error receiving response", e);
218             } catch (InterruptedException e) {
219                 // The thread was interrupted, try and get the value again, this time
220                 // with the remaining time until the deadline.
221                 remainingTime = timeout.minus(
222                         Duration.ofNanos(SystemClock.elapsedRealtimeNanos() - startWaitNanoTime));
223             }
224         }
225         synchronized (sLock) {
226             releaseLocked();
227         }
228         throw new TimeoutException();
229     }
230 
231     ISynchronousResultReceiver mReceiver = null;
232 
233     private final class MyResultReceiver extends ISynchronousResultReceiver.Stub {
send(@uppressWarnings"rawtypes") @onNull Result result)234         public void send(@SuppressWarnings("rawtypes") @NonNull Result result) {
235             @SuppressWarnings("unchecked") Result<T> res = (Result<T>) result;
236             CompletableFuture<Result<T>> future;
237             future = getFuture();
238             if (future != null) {
239                 future.complete(res);
240             }
241         }
242     }
243 
describeContents()244     public int describeContents() {
245         return 0;
246     }
247 
writeToParcel(@onNull Parcel out, int flags)248     public void writeToParcel(@NonNull Parcel out, int flags) {
249         synchronized (this) {
250             if (mReceiver == null) {
251                 mReceiver = new MyResultReceiver();
252             }
253             out.writeStrongBinder(mReceiver.asBinder());
254         }
255     }
256 
SynchronousResultReceiver(Parcel in)257     private SynchronousResultReceiver(Parcel in) {
258         mLocal = false;
259         mIsCompleted = false;
260         mReceiver = ISynchronousResultReceiver.Stub.asInterface(in.readStrongBinder());
261     }
262 
263     public static final @NonNull Parcelable.Creator<SynchronousResultReceiver<?>> CREATOR =
264             new Parcelable.Creator<SynchronousResultReceiver<?>>() {
265             public SynchronousResultReceiver<?> createFromParcel(Parcel in) {
266                 return new SynchronousResultReceiver(in);
267             }
268             public SynchronousResultReceiver<?>[] newArray(int size) {
269                 return new SynchronousResultReceiver[size];
270             }
271         };
272 }
273