1/*
2 * Copyright (C) 2014 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 */
16package android.hardware.camera2.dispatch;
17
18import android.hardware.camera2.utils.UncheckedThrow;
19import android.os.Handler;
20import android.util.Log;
21
22import java.lang.reflect.InvocationTargetException;
23import java.lang.reflect.Method;
24
25import static com.android.internal.util.Preconditions.*;
26
27/**
28 * Forward all interface calls into a handler by posting it as a {@code Runnable}.
29 *
30 * <p>All calls will return immediately; functions with return values will return a default
31 * value of {@code null}, {@code 0}, or {@code false} where that value is legal.</p>
32 *
33 * <p>Any exceptions thrown on the handler while trying to invoke a method
34 * will be re-thrown. Throwing checked exceptions on a handler which doesn't expect any
35 * checked exceptions to be thrown will result in "undefined" behavior
36 * (although in practice it is usually thrown as normal).</p>
37 */
38public class HandlerDispatcher<T> implements Dispatchable<T> {
39
40    private static final String TAG = "HandlerDispatcher";
41
42    private final Dispatchable<T> mDispatchTarget;
43    private final Handler mHandler;
44
45    /**
46     * Create a dispatcher that forwards it's dispatch calls by posting
47     * them onto the {@code handler} as a {@code Runnable}.
48     *
49     * @param dispatchTarget the destination whose method calls will be redirected into the handler
50     * @param handler all calls into {@code dispatchTarget} will be posted onto this handler
51     * @param <T> the type of the element you want to wrap.
52     * @return a dispatcher that will forward it's dispatch calls to a handler
53     */
54    public HandlerDispatcher(Dispatchable<T> dispatchTarget, Handler handler) {
55        mDispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
56        mHandler = checkNotNull(handler, "handler must not be null");
57    }
58
59    @Override
60    public Object dispatch(final Method method, final Object[] args) throws Throwable {
61        mHandler.post(new Runnable() {
62            @Override
63            public void run() {
64                try {
65                    mDispatchTarget.dispatch(method, args);
66                } catch (InvocationTargetException e) {
67                    Throwable t = e.getTargetException();
68                    // Potential UB. Hopefully 't' is a runtime exception.
69                    UncheckedThrow.throwAnyException(t);
70                } catch (IllegalAccessException e) {
71                    // Impossible
72                    Log.wtf(TAG, "IllegalAccessException while invoking " + method, e);
73                } catch (IllegalArgumentException e) {
74                    // Impossible
75                    Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e);
76                } catch (Throwable e) {
77                    UncheckedThrow.throwAnyException(e);
78                }
79            }
80        });
81
82        // TODO handle primitive return values that would avoid NPE if unboxed
83        return null;
84    }
85}
86