summaryrefslogtreecommitdiffstats
path: root/core/java/android/util
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/util')
-rw-r--r--core/java/android/util/TimedRemoteCaller.java134
1 files changed, 134 insertions, 0 deletions
diff --git a/core/java/android/util/TimedRemoteCaller.java b/core/java/android/util/TimedRemoteCaller.java
new file mode 100644
index 0000000..abb2b64
--- /dev/null
+++ b/core/java/android/util/TimedRemoteCaller.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.os.SystemClock;
+
+import java.util.concurrent.TimeoutException;
+
+/**
+ * This is a helper class for making an async one way call and
+ * its async one way response response in a sync fashion within
+ * a timeout. The key idea is to call the remote method with a
+ * sequence number and a callback and then starting to wait for
+ * the response. The remote method calls back with the result and
+ * the sequence number. If the response comes within the timeout
+ * and its sequence number is the one sent in the method invocation,
+ * then the call succeeded. If the response does not come within
+ * the timeout then the call failed. Older result received when
+ * waiting for the result are ignored.
+ * <p>
+ * Typical usage is:
+ * </p>
+ * <p><pre><code>
+ * public class MyMethodCaller extends TimeoutRemoteCallHelper<Object> {
+ * // The one way remote method to call.
+ * private final IRemoteInterface mTarget;
+ *
+ * // One way callback invoked when the remote method is done.
+ * private final IRemoteCallback mCallback = new IRemoteCallback.Stub() {
+ * public void onCompleted(Object result, int sequence) {
+ * onRemoteMethodResult(result, sequence);
+ * }
+ * };
+ *
+ * public MyMethodCaller(IRemoteInterface target) {
+ * mTarget = target;
+ * }
+ *
+ * public Object onCallMyMethod(Object arg) throws RemoteException {
+ * final int sequence = onBeforeRemoteCall();
+ * mTarget.myMethod(arg, sequence);
+ * return getResultTimed(sequence);
+ * }
+ * }
+ * </code></pre></p>
+ *
+ * @param <T> The type of the expected result.
+ *
+ * @hide
+ */
+public abstract class TimedRemoteCaller<T> {
+
+ public static final long DEFAULT_CALL_TIMEOUT_MILLIS = 5000;
+
+ private static final int UNDEFINED_SEQUENCE = -1;
+
+ private final Object mLock = new Object();
+
+ private final long mCallTimeoutMillis;
+
+ private int mSequenceCounter;
+
+ private int mReceivedSequence = UNDEFINED_SEQUENCE;
+
+ private int mAwaitedSequence = UNDEFINED_SEQUENCE;
+
+ private T mResult;
+
+ public TimedRemoteCaller(long callTimeoutMillis) {
+ mCallTimeoutMillis = callTimeoutMillis;
+ }
+
+ public final int onBeforeRemoteCall() {
+ synchronized (mLock) {
+ mAwaitedSequence = mSequenceCounter++;
+ return mAwaitedSequence;
+ }
+ }
+
+ public final T getResultTimed(int sequence) throws TimeoutException {
+ synchronized (mLock) {
+ final boolean success = waitForResultTimedLocked(sequence);
+ if (!success) {
+ throw new TimeoutException("No reponse for sequence: " + sequence);
+ }
+ T result = mResult;
+ mResult = null;
+ return result;
+ }
+ }
+
+ public final void onRemoteMethodResult(T result, int sequence) {
+ synchronized (mLock) {
+ if (sequence == mAwaitedSequence) {
+ mReceivedSequence = sequence;
+ mResult = result;
+ mLock.notifyAll();
+ }
+ }
+ }
+
+ private boolean waitForResultTimedLocked(int sequence) {
+ final long startMillis = SystemClock.uptimeMillis();
+ while (true) {
+ try {
+ if (mReceivedSequence == sequence) {
+ return true;
+ }
+ final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
+ final long waitMillis = mCallTimeoutMillis - elapsedMillis;
+ if (waitMillis <= 0) {
+ return false;
+ }
+ mLock.wait(waitMillis);
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+ }
+}