blob: abb2b6401d7173a3f6bf1270efe8c06d275c1d75 [file] [log] [blame]
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001/*
2 * Copyright (C) 2013 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
17package android.util;
18
19import android.os.SystemClock;
20
21import java.util.concurrent.TimeoutException;
22
23/**
24 * This is a helper class for making an async one way call and
25 * its async one way response response in a sync fashion within
26 * a timeout. The key idea is to call the remote method with a
27 * sequence number and a callback and then starting to wait for
28 * the response. The remote method calls back with the result and
29 * the sequence number. If the response comes within the timeout
30 * and its sequence number is the one sent in the method invocation,
31 * then the call succeeded. If the response does not come within
32 * the timeout then the call failed. Older result received when
33 * waiting for the result are ignored.
34 * <p>
35 * Typical usage is:
36 * </p>
37 * <p><pre><code>
38 * public class MyMethodCaller extends TimeoutRemoteCallHelper<Object> {
39 * // The one way remote method to call.
40 * private final IRemoteInterface mTarget;
41 *
42 * // One way callback invoked when the remote method is done.
43 * private final IRemoteCallback mCallback = new IRemoteCallback.Stub() {
44 * public void onCompleted(Object result, int sequence) {
45 * onRemoteMethodResult(result, sequence);
46 * }
47 * };
48 *
49 * public MyMethodCaller(IRemoteInterface target) {
50 * mTarget = target;
51 * }
52 *
53 * public Object onCallMyMethod(Object arg) throws RemoteException {
54 * final int sequence = onBeforeRemoteCall();
55 * mTarget.myMethod(arg, sequence);
56 * return getResultTimed(sequence);
57 * }
58 * }
59 * </code></pre></p>
60 *
61 * @param <T> The type of the expected result.
62 *
63 * @hide
64 */
65public abstract class TimedRemoteCaller<T> {
66
67 public static final long DEFAULT_CALL_TIMEOUT_MILLIS = 5000;
68
69 private static final int UNDEFINED_SEQUENCE = -1;
70
71 private final Object mLock = new Object();
72
73 private final long mCallTimeoutMillis;
74
75 private int mSequenceCounter;
76
77 private int mReceivedSequence = UNDEFINED_SEQUENCE;
78
79 private int mAwaitedSequence = UNDEFINED_SEQUENCE;
80
81 private T mResult;
82
83 public TimedRemoteCaller(long callTimeoutMillis) {
84 mCallTimeoutMillis = callTimeoutMillis;
85 }
86
87 public final int onBeforeRemoteCall() {
88 synchronized (mLock) {
89 mAwaitedSequence = mSequenceCounter++;
90 return mAwaitedSequence;
91 }
92 }
93
94 public final T getResultTimed(int sequence) throws TimeoutException {
95 synchronized (mLock) {
96 final boolean success = waitForResultTimedLocked(sequence);
97 if (!success) {
98 throw new TimeoutException("No reponse for sequence: " + sequence);
99 }
100 T result = mResult;
101 mResult = null;
102 return result;
103 }
104 }
105
106 public final void onRemoteMethodResult(T result, int sequence) {
107 synchronized (mLock) {
108 if (sequence == mAwaitedSequence) {
109 mReceivedSequence = sequence;
110 mResult = result;
111 mLock.notifyAll();
112 }
113 }
114 }
115
116 private boolean waitForResultTimedLocked(int sequence) {
117 final long startMillis = SystemClock.uptimeMillis();
118 while (true) {
119 try {
120 if (mReceivedSequence == sequence) {
121 return true;
122 }
123 final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
124 final long waitMillis = mCallTimeoutMillis - elapsedMillis;
125 if (waitMillis <= 0) {
126 return false;
127 }
128 mLock.wait(waitMillis);
129 } catch (InterruptedException ie) {
130 /* ignore */
131 }
132 }
133 }
134}