14b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov/*
24b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * Copyright (C) 2013 The Android Open Source Project
34b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *
44b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * Licensed under the Apache License, Version 2.0 (the "License");
54b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * you may not use this file except in compliance with the License.
64b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * You may obtain a copy of the License at
74b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *
84b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *      http://www.apache.org/licenses/LICENSE-2.0
94b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *
104b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * Unless required by applicable law or agreed to in writing, software
114b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * distributed under the License is distributed on an "AS IS" BASIS,
124b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * See the License for the specific language governing permissions and
144b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * limitations under the License.
154b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov */
164b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov
174b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganovpackage android.util;
184b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov
194b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganovimport android.os.SystemClock;
20a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmannimport com.android.internal.annotations.GuardedBy;
214b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov
224b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganovimport java.util.concurrent.TimeoutException;
234b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov
244b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov/**
254b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * This is a helper class for making an async one way call and
264b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * its async one way response response in a sync fashion within
274b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * a timeout. The key idea is to call the remote method with a
284b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * sequence number and a callback and then starting to wait for
294b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * the response. The remote method calls back with the result and
304b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * the sequence number. If the response comes within the timeout
314b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * and its sequence number is the one sent in the method invocation,
324b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * then the call succeeded. If the response does not come within
33a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann * the timeout then the call failed.
344b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * <p>
354b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * Typical usage is:
364b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * </p>
374b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * <p><pre><code>
384b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * public class MyMethodCaller extends TimeoutRemoteCallHelper<Object> {
394b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *     // The one way remote method to call.
404b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *     private final IRemoteInterface mTarget;
414b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *
424b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *     // One way callback invoked when the remote method is done.
434b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *     private final IRemoteCallback mCallback = new IRemoteCallback.Stub() {
444b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *         public void onCompleted(Object result, int sequence) {
454b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *             onRemoteMethodResult(result, sequence);
464b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *         }
474b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *     };
484b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *
494b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *     public MyMethodCaller(IRemoteInterface target) {
504b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *         mTarget = target;
514b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *     }
524b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *
534b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *     public Object onCallMyMethod(Object arg) throws RemoteException {
544b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *         final int sequence = onBeforeRemoteCall();
554b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *         mTarget.myMethod(arg, sequence);
564b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *         return getResultTimed(sequence);
574b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *     }
584b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * }
594b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * </code></pre></p>
604b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *
614b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * @param <T> The type of the expected result.
624b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov *
634b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov * @hide
644b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov */
654b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganovpublic abstract class TimedRemoteCaller<T> {
664b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov
674b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov    public static final long DEFAULT_CALL_TIMEOUT_MILLIS = 5000;
684b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov
694b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov    private final Object mLock = new Object();
704b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov
714b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov    private final long mCallTimeoutMillis;
724b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov
73a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann    /** The callbacks we are waiting for, key == sequence id, value == 1 */
74a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann    @GuardedBy("mLock")
75a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann    private final SparseIntArray mAwaitedCalls = new SparseIntArray(1);
764b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov
77a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann    /** The callbacks we received but for which the result has not yet been reported */
78a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann    @GuardedBy("mLock")
79a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann    private final SparseArray<T> mReceivedCalls = new SparseArray<>(1);
804b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov
81a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann    @GuardedBy("mLock")
82a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann    private int mSequenceCounter;
834b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov
84a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann    /**
85a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     * Create a new timed caller.
86a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     *
87a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     * @param callTimeoutMillis The time to wait in {@link #getResultTimed} before a timed call will
88a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     *                          be declared timed out
89a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     */
904b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov    public TimedRemoteCaller(long callTimeoutMillis) {
914b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov        mCallTimeoutMillis = callTimeoutMillis;
924b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov    }
934b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov
94a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann    /**
95a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     * Indicate that a timed call will be made.
96a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     *
97a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     * @return The sequence id for the call
98a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     */
99a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann    protected final int onBeforeRemoteCall() {
1004b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov        synchronized (mLock) {
101a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann            int sequenceId;
102a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann            do {
103a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann                sequenceId = mSequenceCounter++;
104a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann            } while (mAwaitedCalls.get(sequenceId) != 0);
1054b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov
106a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann            mAwaitedCalls.put(sequenceId, 1);
107a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann
108a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann            return sequenceId;
1094b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov        }
1104b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov    }
1114b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov
112a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann    /**
113a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     * Indicate that the timed call has returned.
114a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     *
115a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     * @param result The result of the timed call
116a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     * @param sequence The sequence id of the call (from {@link #onBeforeRemoteCall()})
117a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     */
118a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann    protected final void onRemoteMethodResult(T result, int sequence) {
1194b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov        synchronized (mLock) {
120a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann            // Do nothing if we do not await the call anymore as it must have timed out
121a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann            boolean containedSequenceId = mAwaitedCalls.get(sequence) != 0;
122a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann            if (containedSequenceId) {
123a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann                mAwaitedCalls.delete(sequence);
124a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann                mReceivedCalls.put(sequence, result);
1254b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov                mLock.notifyAll();
1264b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov            }
1274b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov        }
1284b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov    }
1294b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov
130a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann    /**
131a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     * Wait until the timed call has returned.
132a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     *
133a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     * @param sequence The sequence id of the call (from {@link #onBeforeRemoteCall()})
134a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     *
135a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     * @return The result of the timed call (set in {@link #onRemoteMethodResult(Object, int)})
136a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann     */
137a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann    protected final T getResultTimed(int sequence) throws TimeoutException {
1384b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov        final long startMillis = SystemClock.uptimeMillis();
1394b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov        while (true) {
1404b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov            try {
141a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann                synchronized (mLock) {
142a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann                    if (mReceivedCalls.indexOfKey(sequence) >= 0) {
143a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann                        return mReceivedCalls.removeReturnOld(sequence);
144a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann                    }
145a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann                    final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
146a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann                    final long waitMillis = mCallTimeoutMillis - elapsedMillis;
147a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann                    if (waitMillis <= 0) {
148a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann                        mAwaitedCalls.delete(sequence);
149a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann                        throw new TimeoutException("No response for sequence: " + sequence);
150a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann                    }
151a01bda837b6c50bf051b229051d1d2d4cde5ed68Philip P. Moltmann                    mLock.wait(waitMillis);
1524b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov                }
1534b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov            } catch (InterruptedException ie) {
1544b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov                /* ignore */
1554b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov            }
1564b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov        }
1574b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov    }
1584b9a4d16872bbb50712e007b419ac0b35ff1582dSvetoslav Ganov}
159