From 90d6fccf019b1e8d3687121d3fbdb2c821d18bb5 Mon Sep 17 00:00:00 2001 From: Yin-Chia Yeh Date: Tue, 13 Mar 2018 20:49:12 +0000 Subject: [PATCH] Revert "Camera: SessionConfiguration should use Executors" This reverts commit 004e73c38c799adfe5eaeceb96a5bc9aa3239b31. Bug: 74605221 Bug: 73953366 Change-Id: I1665c3d235434f57a4cd4aa5f082720b83ac10cc --- api/current.txt | 4 +- .../android/hardware/camera2/CameraDevice.java | 3 +- .../dispatch/ArgumentReplacingDispatcher.java | 85 ++++++++++ .../camera2/dispatch/BroadcastDispatcher.java | 64 ++++++++ .../hardware/camera2/dispatch/Dispatchable.java | 35 ++++ .../camera2/dispatch/DuckTypingDispatcher.java | 55 +++++++ .../camera2/dispatch/HandlerDispatcher.java | 85 ++++++++++ .../camera2/dispatch/InvokeDispatcher.java | 55 +++++++ .../camera2/dispatch/MethodNameInvoker.java | 97 +++++++++++ .../hardware/camera2/dispatch/NullDispatcher.java | 38 +++++ .../android/hardware/camera2/dispatch/package.html | 3 + .../hardware/camera2/impl/CallbackProxies.java | 179 +++++++++++++++------ .../camera2/impl/CameraCaptureSessionImpl.java | 164 ++++++++----------- ...meraConstrainedHighSpeedCaptureSessionImpl.java | 5 +- .../hardware/camera2/impl/CameraDeviceImpl.java | 179 +++++++-------------- .../camera2/params/SessionConfiguration.java | 25 +-- .../hardware/camera2/utils/TaskDrainer.java | 26 +-- .../hardware/camera2/utils/TaskSingleDrainer.java | 19 ++- 18 files changed, 812 insertions(+), 309 deletions(-) create mode 100644 core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java create mode 100644 core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java create mode 100644 core/java/android/hardware/camera2/dispatch/Dispatchable.java create mode 100644 core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java create mode 100644 core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java create mode 100644 core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java create mode 100644 core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java create mode 100644 core/java/android/hardware/camera2/dispatch/NullDispatcher.java create mode 100644 core/java/android/hardware/camera2/dispatch/package.html diff --git a/api/current.txt b/api/current.txt index e0b01cac3c13..72666062c7fe 100644 --- a/api/current.txt +++ b/api/current.txt @@ -16425,8 +16425,8 @@ package android.hardware.camera2.params { } public final class SessionConfiguration { - ctor public SessionConfiguration(int, java.util.List, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.StateCallback); - method public java.util.concurrent.Executor getExecutor(); + ctor public SessionConfiguration(int, java.util.List, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler); + method public android.os.Handler getHandler(); method public android.hardware.camera2.params.InputConfiguration getInputConfiguration(); method public java.util.List getOutputConfigurations(); method public android.hardware.camera2.CaptureRequest getSessionParameters(); diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index f47d4640c09c..72db33f90c20 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -819,8 +819,7 @@ public abstract class CameraDevice implements AutoCloseable { * @param config A session configuration (see {@link SessionConfiguration}). * * @throws IllegalArgumentException In case the session configuration is invalid; or the output - * configurations are empty; or the session configuration - * executor is invalid. + * configurations are empty. * @throws CameraAccessException In case the camera device is no longer connected or has * encountered a fatal error. * @see #createCaptureSession(List, CameraCaptureSession.StateCallback, Handler) diff --git a/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java b/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java new file mode 100644 index 000000000000..866f370f5d5d --- /dev/null +++ b/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.dispatch; + +import java.lang.reflect.Method; + +import static com.android.internal.util.Preconditions.*; + +/** + * A dispatcher that replaces one argument with another; replaces any argument at an index + * with another argument. + * + *

For example, we can override an {@code void onSomething(int x)} calls to have {@code x} always + * equal to 1. Or, if using this with a duck typing dispatcher, we could even overwrite {@code x} to + * be something + * that's not an {@code int}.

+ * + * @param + * source dispatch type, whose methods with {@link #dispatch} will be called + * @param + * argument replacement type, args in {@link #dispatch} matching {@code argumentIndex} + * will be overriden to objects of this type + */ +public class ArgumentReplacingDispatcher implements Dispatchable { + + private final Dispatchable mTarget; + private final int mArgumentIndex; + private final TArg mReplaceWith; + + /** + * Create a new argument replacing dispatcher; dispatches are forwarded to {@code target} + * after the argument is replaced. + * + *

For example, if a method {@code onAction(T1 a, Integer b, T2 c)} is invoked, and we wanted + * to replace all occurrences of {@code b} with {@code 0xDEADBEEF}, we would set + * {@code argumentIndex = 1} and {@code replaceWith = 0xDEADBEEF}.

+ * + *

If a method dispatched has less arguments than {@code argumentIndex}, it is + * passed through with the arguments unchanged.

+ * + * @param target destination dispatch type, methods will be redirected to this dispatcher + * @param argumentIndex the numeric index of the argument {@code >= 0} + * @param replaceWith arguments matching {@code argumentIndex} will be replaced with this object + */ + public ArgumentReplacingDispatcher(Dispatchable target, int argumentIndex, + TArg replaceWith) { + mTarget = checkNotNull(target, "target must not be null"); + mArgumentIndex = checkArgumentNonnegative(argumentIndex, + "argumentIndex must not be negative"); + mReplaceWith = checkNotNull(replaceWith, "replaceWith must not be null"); + } + + @Override + public Object dispatch(Method method, Object[] args) throws Throwable { + + if (args.length > mArgumentIndex) { + args = arrayCopy(args); // don't change in-place since it can affect upstream dispatches + args[mArgumentIndex] = mReplaceWith; + } + + return mTarget.dispatch(method, args); + } + + private static Object[] arrayCopy(Object[] array) { + int length = array.length; + Object[] newArray = new Object[length]; + for (int i = 0; i < length; ++i) { + newArray[i] = array[i]; + } + return newArray; + } +} diff --git a/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java b/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java new file mode 100644 index 000000000000..fe575b277616 --- /dev/null +++ b/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.dispatch; + + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; + +import static com.android.internal.util.Preconditions.*; + +/** + * Broadcast a single dispatch into multiple other dispatchables. + * + *

Every time {@link #dispatch} is invoked, all the broadcast targets will + * see the same dispatch as well. The first target's return value is returned.

+ * + *

This enables a single listener to be converted into a multi-listener.

+ */ +public class BroadcastDispatcher implements Dispatchable { + + private final List> mDispatchTargets; + + /** + * Create a broadcast dispatcher from the supplied dispatch targets. + * + * @param dispatchTargets one or more targets to dispatch to + */ + @SafeVarargs + public BroadcastDispatcher(Dispatchable... dispatchTargets) { + mDispatchTargets = Arrays.asList( + checkNotNull(dispatchTargets, "dispatchTargets must not be null")); + } + + @Override + public Object dispatch(Method method, Object[] args) throws Throwable { + Object result = null; + boolean gotResult = false; + + for (Dispatchable dispatchTarget : mDispatchTargets) { + Object localResult = dispatchTarget.dispatch(method, args); + + if (!gotResult) { + gotResult = true; + result = localResult; + } + } + + return result; + } +} diff --git a/core/java/android/hardware/camera2/dispatch/Dispatchable.java b/core/java/android/hardware/camera2/dispatch/Dispatchable.java new file mode 100644 index 000000000000..753103f45ea8 --- /dev/null +++ b/core/java/android/hardware/camera2/dispatch/Dispatchable.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.dispatch; + +import java.lang.reflect.Method; + +/** + * Dynamically dispatch a method and its argument to some object. + * + *

This can be used to intercept method calls and do work around them, redirect work, + * or block calls entirely.

+ */ +public interface Dispatchable { + /** + * Dispatch the method and arguments to this object. + * @param method a method defined in class {@code T} + * @param args arguments corresponding to said {@code method} + * @return the object returned when invoking {@code method} + * @throws Throwable any exception that might have been raised while invoking the method + */ + public Object dispatch(Method method, Object[] args) throws Throwable; +} diff --git a/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java b/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java new file mode 100644 index 000000000000..75f97e4656aa --- /dev/null +++ b/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.dispatch; + + +import java.lang.reflect.Method; + +import static com.android.internal.util.Preconditions.*; + +/** + * Duck typing dispatcher; converts dispatch methods calls from one class to another by + * looking up equivalently methods at runtime by name. + * + *

For example, if two types have identical method names and arguments, but + * are not subclasses/subinterfaces of each other, this dispatcher will allow calls to be + * made from one type to the other.

+ * + * @param source dispatch type, whose methods with {@link #dispatch} will be called + * @param destination dispatch type, methods will be converted to the class of {@code T} + */ +public class DuckTypingDispatcher implements Dispatchable { + + private final MethodNameInvoker mDuck; + + /** + * Create a new duck typing dispatcher. + * + * @param target destination dispatch type, methods will be redirected to this dispatcher + * @param targetClass destination dispatch class, methods will be converted to this class's + */ + public DuckTypingDispatcher(Dispatchable target, Class targetClass) { + checkNotNull(targetClass, "targetClass must not be null"); + checkNotNull(target, "target must not be null"); + + mDuck = new MethodNameInvoker(target, targetClass); + } + + @Override + public Object dispatch(Method method, Object[] args) { + return mDuck.invoke(method.getName(), args); + } +} diff --git a/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java b/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java new file mode 100644 index 000000000000..f8e9d49a95c6 --- /dev/null +++ b/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.dispatch; + +import android.hardware.camera2.utils.UncheckedThrow; +import android.os.Handler; +import android.util.Log; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import static com.android.internal.util.Preconditions.*; + +/** + * Forward all interface calls into a handler by posting it as a {@code Runnable}. + * + *

All calls will return immediately; functions with return values will return a default + * value of {@code null}, {@code 0}, or {@code false} where that value is legal.

+ * + *

Any exceptions thrown on the handler while trying to invoke a method + * will be re-thrown. Throwing checked exceptions on a handler which doesn't expect any + * checked exceptions to be thrown will result in "undefined" behavior + * (although in practice it is usually thrown as normal).

+ */ +public class HandlerDispatcher implements Dispatchable { + + private static final String TAG = "HandlerDispatcher"; + + private final Dispatchable mDispatchTarget; + private final Handler mHandler; + + /** + * Create a dispatcher that forwards it's dispatch calls by posting + * them onto the {@code handler} as a {@code Runnable}. + * + * @param dispatchTarget the destination whose method calls will be redirected into the handler + * @param handler all calls into {@code dispatchTarget} will be posted onto this handler + * @param the type of the element you want to wrap. + * @return a dispatcher that will forward it's dispatch calls to a handler + */ + public HandlerDispatcher(Dispatchable dispatchTarget, Handler handler) { + mDispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null"); + mHandler = checkNotNull(handler, "handler must not be null"); + } + + @Override + public Object dispatch(final Method method, final Object[] args) throws Throwable { + mHandler.post(new Runnable() { + @Override + public void run() { + try { + mDispatchTarget.dispatch(method, args); + } catch (InvocationTargetException e) { + Throwable t = e.getTargetException(); + // Potential UB. Hopefully 't' is a runtime exception. + UncheckedThrow.throwAnyException(t); + } catch (IllegalAccessException e) { + // Impossible + Log.wtf(TAG, "IllegalAccessException while invoking " + method, e); + } catch (IllegalArgumentException e) { + // Impossible + Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e); + } catch (Throwable e) { + UncheckedThrow.throwAnyException(e); + } + } + }); + + // TODO handle primitive return values that would avoid NPE if unboxed + return null; + } +} diff --git a/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java b/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java new file mode 100644 index 000000000000..ac5f52676df4 --- /dev/null +++ b/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.dispatch; + +import android.hardware.camera2.utils.UncheckedThrow; +import android.util.Log; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import static com.android.internal.util.Preconditions.*; + + +public class InvokeDispatcher implements Dispatchable { + + private static final String TAG = "InvocationSink"; + private final T mTarget; + + public InvokeDispatcher(T target) { + mTarget = checkNotNull(target, "target must not be null"); + } + + @Override + public Object dispatch(Method method, Object[] args) { + try { + return method.invoke(mTarget, args); + } catch (InvocationTargetException e) { + Throwable t = e.getTargetException(); + // Potential UB. Hopefully 't' is a runtime exception. + UncheckedThrow.throwAnyException(t); + } catch (IllegalAccessException e) { + // Impossible + Log.wtf(TAG, "IllegalAccessException while invoking " + method, e); + } catch (IllegalArgumentException e) { + // Impossible + Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e); + } + + // unreachable + return null; + } +} diff --git a/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java b/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java new file mode 100644 index 000000000000..8296b7a915a4 --- /dev/null +++ b/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.dispatch; + +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.hardware.camera2.utils.UncheckedThrow; + +import java.lang.reflect.Method; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Invoke a method on a dispatchable by its name (without knowing the {@code Method} ahead of time). + * + * @param destination dispatch type, methods will be looked up in the class of {@code T} + */ +public class MethodNameInvoker { + + private final Dispatchable mTarget; + private final Class mTargetClass; + private final Method[] mTargetClassMethods; + private final ConcurrentHashMap mMethods = + new ConcurrentHashMap<>(); + + /** + * Create a new method name invoker. + * + * @param target destination dispatch type, invokes will be redirected to this dispatcher + * @param targetClass destination dispatch class, the invoked methods will be from this class + */ + public MethodNameInvoker(Dispatchable target, Class targetClass) { + mTargetClass = targetClass; + mTargetClassMethods = targetClass.getMethods(); + mTarget = target; + } + + /** + * Invoke a method by its name. + * + *

If more than one method exists in {@code targetClass}, the first method with the right + * number of arguments will be used, and later calls will all use that method.

+ * + * @param methodName + * The name of the method, which will be matched 1:1 to the destination method + * @param params + * Variadic parameter list. + * @return + * The same kind of value that would normally be returned by calling {@code methodName} + * statically. + * + * @throws IllegalArgumentException if {@code methodName} does not exist on the target class + * @throws Throwable will rethrow anything that the target method would normally throw + */ + @SuppressWarnings("unchecked") + public K invoke(String methodName, Object... params) { + checkNotNull(methodName, "methodName must not be null"); + + Method targetMethod = mMethods.get(methodName); + if (targetMethod == null) { + for (Method method : mTargetClassMethods) { + // TODO future: match types of params if possible + if (method.getName().equals(methodName) && + (params.length == method.getParameterTypes().length) ) { + targetMethod = method; + mMethods.put(methodName, targetMethod); + break; + } + } + + if (targetMethod == null) { + throw new IllegalArgumentException( + "Method " + methodName + " does not exist on class " + mTargetClass); + } + } + + try { + return (K) mTarget.dispatch(targetMethod, params); + } catch (Throwable e) { + UncheckedThrow.throwAnyException(e); + // unreachable + return null; + } + } +} diff --git a/core/java/android/hardware/camera2/dispatch/NullDispatcher.java b/core/java/android/hardware/camera2/dispatch/NullDispatcher.java new file mode 100644 index 000000000000..fada075cba67 --- /dev/null +++ b/core/java/android/hardware/camera2/dispatch/NullDispatcher.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014 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.hardware.camera2.dispatch; + + +import java.lang.reflect.Method; + +/** + * Do nothing when dispatching; follows the null object pattern. + */ +public class NullDispatcher implements Dispatchable { + /** + * Create a dispatcher that does nothing when dispatched to. + */ + public NullDispatcher() { + } + + /** + * Do nothing; all parameters are ignored. + */ + @Override + public Object dispatch(Method method, Object[] args) { + return null; + } +} diff --git a/core/java/android/hardware/camera2/dispatch/package.html b/core/java/android/hardware/camera2/dispatch/package.html new file mode 100644 index 000000000000..783d0a1b54d5 --- /dev/null +++ b/core/java/android/hardware/camera2/dispatch/package.html @@ -0,0 +1,3 @@ + +{@hide} + diff --git a/core/java/android/hardware/camera2/impl/CallbackProxies.java b/core/java/android/hardware/camera2/impl/CallbackProxies.java index 9e4cb80b89f0..c9eecf10e3d3 100644 --- a/core/java/android/hardware/camera2/impl/CallbackProxies.java +++ b/core/java/android/hardware/camera2/impl/CallbackProxies.java @@ -15,17 +15,16 @@ */ package android.hardware.camera2.impl; -import android.os.Binder; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.dispatch.Dispatchable; +import android.hardware.camera2.dispatch.MethodNameInvoker; import android.view.Surface; -import java.util.concurrent.Executor; - import static com.android.internal.util.Preconditions.*; /** @@ -35,86 +34,164 @@ import static com.android.internal.util.Preconditions.*; * to use our own proxy mechanism.

*/ public class CallbackProxies { + + // TODO: replace with codegen + + public static class DeviceStateCallbackProxy extends CameraDeviceImpl.StateCallbackKK { + private final MethodNameInvoker mProxy; + + public DeviceStateCallbackProxy( + Dispatchable dispatchTarget) { + dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null"); + mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.StateCallbackKK.class); + } + + @Override + public void onOpened(CameraDevice camera) { + mProxy.invoke("onOpened", camera); + } + + @Override + public void onDisconnected(CameraDevice camera) { + mProxy.invoke("onDisconnected", camera); + } + + @Override + public void onError(CameraDevice camera, int error) { + mProxy.invoke("onError", camera, error); + } + + @Override + public void onUnconfigured(CameraDevice camera) { + mProxy.invoke("onUnconfigured", camera); + } + + @Override + public void onActive(CameraDevice camera) { + mProxy.invoke("onActive", camera); + } + + @Override + public void onBusy(CameraDevice camera) { + mProxy.invoke("onBusy", camera); + } + + @Override + public void onClosed(CameraDevice camera) { + mProxy.invoke("onClosed", camera); + } + + @Override + public void onIdle(CameraDevice camera) { + mProxy.invoke("onIdle", camera); + } + } + + @SuppressWarnings("deprecation") + public static class DeviceCaptureCallbackProxy implements CameraDeviceImpl.CaptureCallback { + private final MethodNameInvoker mProxy; + + public DeviceCaptureCallbackProxy( + Dispatchable dispatchTarget) { + dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null"); + mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.CaptureCallback.class); + } + + @Override + public void onCaptureStarted(CameraDevice camera, + CaptureRequest request, long timestamp, long frameNumber) { + mProxy.invoke("onCaptureStarted", camera, request, timestamp, frameNumber); + } + + @Override + public void onCapturePartial(CameraDevice camera, + CaptureRequest request, CaptureResult result) { + mProxy.invoke("onCapturePartial", camera, request, result); + } + + @Override + public void onCaptureProgressed(CameraDevice camera, + CaptureRequest request, CaptureResult partialResult) { + mProxy.invoke("onCaptureProgressed", camera, request, partialResult); + } + + @Override + public void onCaptureCompleted(CameraDevice camera, + CaptureRequest request, TotalCaptureResult result) { + mProxy.invoke("onCaptureCompleted", camera, request, result); + } + + @Override + public void onCaptureFailed(CameraDevice camera, + CaptureRequest request, CaptureFailure failure) { + mProxy.invoke("onCaptureFailed", camera, request, failure); + } + + @Override + public void onCaptureSequenceCompleted(CameraDevice camera, + int sequenceId, long frameNumber) { + mProxy.invoke("onCaptureSequenceCompleted", camera, sequenceId, frameNumber); + } + + @Override + public void onCaptureSequenceAborted(CameraDevice camera, + int sequenceId) { + mProxy.invoke("onCaptureSequenceAborted", camera, sequenceId); + } + + @Override + public void onCaptureBufferLost(CameraDevice camera, + CaptureRequest request, Surface target, long frameNumber) { + mProxy.invoke("onCaptureBufferLost", camera, request, target, frameNumber); + } + + } + public static class SessionStateCallbackProxy extends CameraCaptureSession.StateCallback { - private final Executor mExecutor; - private final CameraCaptureSession.StateCallback mCallback; + private final MethodNameInvoker mProxy; - public SessionStateCallbackProxy(Executor executor, - CameraCaptureSession.StateCallback callback) { - mExecutor = checkNotNull(executor, "executor must not be null"); - mCallback = checkNotNull(callback, "callback must not be null"); + public SessionStateCallbackProxy( + Dispatchable dispatchTarget) { + dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null"); + mProxy = new MethodNameInvoker<>(dispatchTarget, + CameraCaptureSession.StateCallback.class); } @Override public void onConfigured(CameraCaptureSession session) { - final long ident = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onConfigured(session)); - } finally { - Binder.restoreCallingIdentity(ident); - } + mProxy.invoke("onConfigured", session); } @Override public void onConfigureFailed(CameraCaptureSession session) { - final long ident = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onConfigureFailed(session)); - } finally { - Binder.restoreCallingIdentity(ident); - } + mProxy.invoke("onConfigureFailed", session); } @Override public void onReady(CameraCaptureSession session) { - final long ident = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onReady(session)); - } finally { - Binder.restoreCallingIdentity(ident); - } + mProxy.invoke("onReady", session); } @Override public void onActive(CameraCaptureSession session) { - final long ident = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onActive(session)); - } finally { - Binder.restoreCallingIdentity(ident); - } + mProxy.invoke("onActive", session); } @Override public void onCaptureQueueEmpty(CameraCaptureSession session) { - final long ident = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onCaptureQueueEmpty(session)); - } finally { - Binder.restoreCallingIdentity(ident); - } + mProxy.invoke("onCaptureQueueEmpty", session); } @Override public void onClosed(CameraCaptureSession session) { - final long ident = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onClosed(session)); - } finally { - Binder.restoreCallingIdentity(ident); - } + mProxy.invoke("onClosed", session); } @Override public void onSurfacePrepared(CameraCaptureSession session, Surface surface) { - final long ident = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onSurfacePrepared(session, surface)); - } finally { - Binder.restoreCallingIdentity(ident); - } + mProxy.invoke("onSurfacePrepared", session, surface); } } diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index f3f6c84a7e0b..8b8bbc34f7d2 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -20,18 +20,20 @@ import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.ICameraDeviceUser; +import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher; +import android.hardware.camera2.dispatch.BroadcastDispatcher; +import android.hardware.camera2.dispatch.DuckTypingDispatcher; +import android.hardware.camera2.dispatch.HandlerDispatcher; +import android.hardware.camera2.dispatch.InvokeDispatcher; import android.hardware.camera2.params.OutputConfiguration; import android.hardware.camera2.utils.TaskDrainer; import android.hardware.camera2.utils.TaskSingleDrainer; -import android.os.Binder; import android.os.Handler; -import android.os.HandlerExecutor; import android.util.Log; import android.view.Surface; import java.util.Arrays; import java.util.List; -import java.util.concurrent.Executor; import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler; import static com.android.internal.util.Preconditions.*; @@ -49,11 +51,11 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession private final Surface mInput; /** * User-specified state callback, used for outgoing events; calls to this object will be - * automatically invoked via {@code mStateExecutor}. + * automatically {@link Handler#post(Runnable) posted} to {@code mStateHandler}. */ private final CameraCaptureSession.StateCallback mStateCallback; - /** User-specified state executor used for outgoing state callback events */ - private final Executor mStateExecutor; + /** User-specified state handler used for outgoing state callback events */ + private final Handler mStateHandler; /** Internal camera device; used to translate calls into existing deprecated API */ private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl; @@ -85,7 +87,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession * (e.g. no pending captures, no repeating requests, no flush).

*/ CameraCaptureSessionImpl(int id, Surface input, - CameraCaptureSession.StateCallback callback, Executor stateExecutor, + CameraCaptureSession.StateCallback callback, Handler stateHandler, android.hardware.camera2.impl.CameraDeviceImpl deviceImpl, Handler deviceStateHandler, boolean configureSuccess) { if (callback == null) { @@ -96,8 +98,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession mIdString = String.format("Session %d: ", mId); mInput = input; - mStateExecutor = checkNotNull(stateExecutor, "stateExecutor must not be null"); - mStateCallback = createUserStateCallbackProxy(mStateExecutor, callback); + mStateHandler = checkHandler(stateHandler); + mStateCallback = createUserStateCallbackProxy(mStateHandler, callback); mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null"); mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null"); @@ -108,12 +110,12 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession * This ensures total ordering between CameraDevice.StateCallback and * CameraDeviceImpl.CaptureCallback events. */ - mSequenceDrainer = new TaskDrainer<>(new HandlerExecutor(mDeviceHandler), - new SequenceDrainListener(), /*name*/"seq"); - mIdleDrainer = new TaskSingleDrainer(new HandlerExecutor(mDeviceHandler), - new IdleDrainListener(), /*name*/"idle"); - mAbortDrainer = new TaskSingleDrainer(new HandlerExecutor(mDeviceHandler), - new AbortDrainListener(), /*name*/"abort"); + mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(), + /*name*/"seq"); + mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(), + /*name*/"idle"); + mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(), + /*name*/"abort"); // CameraDevice should call configureOutputs and have it finish before constructing us @@ -444,140 +446,114 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession } /** - * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code executor}. + * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code handler}. */ - private StateCallback createUserStateCallbackProxy(Executor executor, StateCallback callback) { - return new CallbackProxies.SessionStateCallbackProxy(executor, callback); + private StateCallback createUserStateCallbackProxy(Handler handler, StateCallback callback) { + InvokeDispatcher userCallbackSink = new InvokeDispatcher<>(callback); + HandlerDispatcher handlerPassthrough = + new HandlerDispatcher<>(userCallbackSink, handler); + + return new CallbackProxies.SessionStateCallbackProxy(handlerPassthrough); } /** * Forward callbacks from * CameraDeviceImpl.CaptureCallback to the CameraCaptureSession.CaptureCallback. * + *

In particular, all calls are automatically split to go both to our own + * internal callback, and to the user-specified callback (by transparently posting + * to the user-specified handler).

+ * *

When a capture sequence finishes, update the pending checked sequences set.

*/ @SuppressWarnings("deprecation") private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy( Handler handler, CaptureCallback callback) { - final Executor executor = (callback != null) ? CameraDeviceImpl.checkAndWrapHandler( - handler) : null; + CameraDeviceImpl.CaptureCallback localCallback = new CameraDeviceImpl.CaptureCallback() { - return new CameraDeviceImpl.CaptureCallback() { @Override public void onCaptureStarted(CameraDevice camera, CaptureRequest request, long timestamp, long frameNumber) { - if ((callback != null) && (executor != null)) { - final long ident = Binder.clearCallingIdentity(); - try { - executor.execute(() -> callback.onCaptureStarted( - CameraCaptureSessionImpl.this, request, timestamp, - frameNumber)); - } finally { - Binder.restoreCallingIdentity(ident); - } - } + // Do nothing } @Override public void onCapturePartial(CameraDevice camera, CaptureRequest request, android.hardware.camera2.CaptureResult result) { - if ((callback != null) && (executor != null)) { - final long ident = Binder.clearCallingIdentity(); - try { - executor.execute(() -> callback.onCapturePartial( - CameraCaptureSessionImpl.this, request, result)); - } finally { - Binder.restoreCallingIdentity(ident); - } - } + // Do nothing } @Override public void onCaptureProgressed(CameraDevice camera, CaptureRequest request, android.hardware.camera2.CaptureResult partialResult) { - if ((callback != null) && (executor != null)) { - final long ident = Binder.clearCallingIdentity(); - try { - executor.execute(() -> callback.onCaptureProgressed( - CameraCaptureSessionImpl.this, request, partialResult)); - } finally { - Binder.restoreCallingIdentity(ident); - } - } + // Do nothing } @Override public void onCaptureCompleted(CameraDevice camera, CaptureRequest request, android.hardware.camera2.TotalCaptureResult result) { - if ((callback != null) && (executor != null)) { - final long ident = Binder.clearCallingIdentity(); - try { - executor.execute(() -> callback.onCaptureCompleted( - CameraCaptureSessionImpl.this, request, result)); - } finally { - Binder.restoreCallingIdentity(ident); - } - } + // Do nothing } @Override public void onCaptureFailed(CameraDevice camera, CaptureRequest request, android.hardware.camera2.CaptureFailure failure) { - if ((callback != null) && (executor != null)) { - final long ident = Binder.clearCallingIdentity(); - try { - executor.execute(() -> callback.onCaptureFailed( - CameraCaptureSessionImpl.this, request, failure)); - } finally { - Binder.restoreCallingIdentity(ident); - } - } + // Do nothing } @Override public void onCaptureSequenceCompleted(CameraDevice camera, int sequenceId, long frameNumber) { - if ((callback != null) && (executor != null)) { - final long ident = Binder.clearCallingIdentity(); - try { - executor.execute(() -> callback.onCaptureSequenceCompleted( - CameraCaptureSessionImpl.this, sequenceId, frameNumber)); - } finally { - Binder.restoreCallingIdentity(ident); - } - } finishPendingSequence(sequenceId); } @Override public void onCaptureSequenceAborted(CameraDevice camera, int sequenceId) { - if ((callback != null) && (executor != null)) { - final long ident = Binder.clearCallingIdentity(); - try { - executor.execute(() -> callback.onCaptureSequenceAborted( - CameraCaptureSessionImpl.this, sequenceId)); - } finally { - Binder.restoreCallingIdentity(ident); - } - } finishPendingSequence(sequenceId); } @Override public void onCaptureBufferLost(CameraDevice camera, CaptureRequest request, Surface target, long frameNumber) { - if ((callback != null) && (executor != null)) { - final long ident = Binder.clearCallingIdentity(); - try { - executor.execute(() -> callback.onCaptureBufferLost( - CameraCaptureSessionImpl.this, request, target, frameNumber)); - } finally { - Binder.restoreCallingIdentity(ident); - } - } + // Do nothing } + }; + + /* + * Split the calls from the device callback into local callback and the following chain: + * - replace the first CameraDevice arg with a CameraCaptureSession + * - duck type from device callback to session callback + * - then forward the call to a handler + * - then finally invoke the destination method on the session callback object + */ + if (callback == null) { + // OK: API allows the user to not specify a callback, and the handler may + // also be null in that case. Collapse whole dispatch chain to only call the local + // callback + return localCallback; + } + + InvokeDispatcher localSink = + new InvokeDispatcher<>(localCallback); + + InvokeDispatcher userCallbackSink = + new InvokeDispatcher<>(callback); + HandlerDispatcher handlerPassthrough = + new HandlerDispatcher<>(userCallbackSink, handler); + DuckTypingDispatcher duckToSession + = new DuckTypingDispatcher<>(handlerPassthrough, CaptureCallback.class); + ArgumentReplacingDispatcher + replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession, + /*argumentIndex*/0, this); + + BroadcastDispatcher broadcaster = + new BroadcastDispatcher( + replaceDeviceWithSession, + localSink); + + return new CallbackProxies.DeviceCaptureCallbackProxy(broadcaster); } /** diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java index 4ee08bae84cb..06c2c25ab6bb 100644 --- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java @@ -33,7 +33,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.concurrent.Executor; import static com.android.internal.util.Preconditions.*; @@ -60,14 +59,14 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl * (e.g. no pending captures, no repeating requests, no flush).

*/ CameraConstrainedHighSpeedCaptureSessionImpl(int id, - CameraCaptureSession.StateCallback callback, Executor stateExecutor, + CameraCaptureSession.StateCallback callback, Handler stateHandler, android.hardware.camera2.impl.CameraDeviceImpl deviceImpl, Handler deviceStateHandler, boolean configureSuccess, CameraCharacteristics characteristics) { mCharacteristics = characteristics; CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback); mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, wrapperCallback, - stateExecutor, deviceImpl, deviceStateHandler, configureSuccess); + stateHandler, deviceImpl, deviceStateHandler, configureSuccess); } @Override diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index b328bb1726e9..511fa43a5441 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -35,10 +35,8 @@ import android.hardware.camera2.params.SessionConfiguration; import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.utils.SubmitInfo; import android.hardware.camera2.utils.SurfaceUtils; -import android.os.Binder; import android.os.Build; import android.os.Handler; -import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; @@ -60,7 +58,6 @@ import java.util.List; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.Executor; /** * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate @@ -504,9 +501,8 @@ public class CameraDeviceImpl extends CameraDevice for (Surface surface : outputs) { outConfigurations.add(new OutputConfiguration(surface)); } - createCaptureSessionInternal(null, outConfigurations, callback, - checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, - /*sessionParams*/ null); + createCaptureSessionInternal(null, outConfigurations, callback, handler, + /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null); } @Override @@ -521,7 +517,7 @@ public class CameraDeviceImpl extends CameraDevice // OutputConfiguration objects are immutable, but need to have our own array List currentOutputs = new ArrayList<>(outputConfigurations); - createCaptureSessionInternal(null, currentOutputs, callback, checkAndWrapHandler(handler), + createCaptureSessionInternal(null, currentOutputs, callback, handler, /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/null); } @@ -541,9 +537,8 @@ public class CameraDeviceImpl extends CameraDevice for (Surface surface : outputs) { outConfigurations.add(new OutputConfiguration(surface)); } - createCaptureSessionInternal(inputConfig, outConfigurations, callback, - checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, - /*sessionParams*/ null); + createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler, + /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null); } @Override @@ -571,8 +566,8 @@ public class CameraDeviceImpl extends CameraDevice currentOutputs.add(new OutputConfiguration(output)); } createCaptureSessionInternal(inputConfig, currentOutputs, - callback, checkAndWrapHandler(handler), - /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null); + callback, handler, /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, + /*sessionParams*/ null); } @Override @@ -587,8 +582,7 @@ public class CameraDeviceImpl extends CameraDevice for (Surface surface : outputs) { outConfigurations.add(new OutputConfiguration(surface)); } - createCaptureSessionInternal(null, outConfigurations, callback, - checkAndWrapHandler(handler), + createCaptureSessionInternal(null, outConfigurations, callback, handler, /*operatingMode*/ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE, /*sessionParams*/ null); } @@ -603,8 +597,8 @@ public class CameraDeviceImpl extends CameraDevice for (OutputConfiguration output : outputs) { currentOutputs.add(new OutputConfiguration(output)); } - createCaptureSessionInternal(inputConfig, currentOutputs, callback, - checkAndWrapHandler(handler), operatingMode, /*sessionParams*/ null); + createCaptureSessionInternal(inputConfig, currentOutputs, callback, handler, operatingMode, + /*sessionParams*/ null); } @Override @@ -618,17 +612,14 @@ public class CameraDeviceImpl extends CameraDevice if (outputConfigs == null) { throw new IllegalArgumentException("Invalid output configurations"); } - if (config.getExecutor() == null) { - throw new IllegalArgumentException("Invalid executor"); - } createCaptureSessionInternal(config.getInputConfiguration(), outputConfigs, - config.getStateCallback(), config.getExecutor(), config.getSessionType(), + config.getStateCallback(), config.getHandler(), config.getSessionType(), config.getSessionParameters()); } private void createCaptureSessionInternal(InputConfiguration inputConfig, List outputConfigurations, - CameraCaptureSession.StateCallback callback, Executor executor, + CameraCaptureSession.StateCallback callback, Handler handler, int operatingMode, CaptureRequest sessionParams) throws CameraAccessException { synchronized(mInterfaceLock) { if (DEBUG) { @@ -682,11 +673,12 @@ public class CameraDeviceImpl extends CameraDevice SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config); newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++, - callback, executor, this, mDeviceHandler, configureSuccess, + callback, handler, this, mDeviceHandler, configureSuccess, mCharacteristics); } else { newSession = new CameraCaptureSessionImpl(mNextSessionId++, input, - callback, executor, this, mDeviceHandler, configureSuccess); + callback, handler, this, mDeviceHandler, + configureSuccess); } // TODO: wait until current session closes, then create the new session @@ -971,12 +963,7 @@ public class CameraDeviceImpl extends CameraDevice } } }; - final long ident = Binder.clearCallingIdentity(); - try { - holder.getExecutor().execute(resultDispatch); - } finally { - Binder.restoreCallingIdentity(ident); - } + holder.getHandler().post(resultDispatch); } else { Log.w(TAG, String.format( "did not register callback to request %d", @@ -997,9 +984,9 @@ public class CameraDeviceImpl extends CameraDevice private int submitCaptureRequest(List requestList, CaptureCallback callback, Handler handler, boolean repeating) throws CameraAccessException { - // Need a valid executor, or current thread needs to have a looper, if + // Need a valid handler, or current thread needs to have a looper, if // callback is valid - Executor executor = getExecutor(handler, callback); + handler = checkHandler(handler, callback); // Make sure that there all requests have at least 1 surface; all surfaces are non-null; // the surface isn't a physical stream surface for reprocessing request @@ -1053,7 +1040,7 @@ public class CameraDeviceImpl extends CameraDevice if (callback != null) { mCaptureCallbackMap.put(requestInfo.getRequestId(), new CaptureCallbackHolder( - callback, requestList, executor, repeating, mNextSessionId - 1)); + callback, requestList, handler, repeating, mNextSessionId - 1)); } else { if (DEBUG) { Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null"); @@ -1367,7 +1354,7 @@ public class CameraDeviceImpl extends CameraDevice private final boolean mRepeating; private final CaptureCallback mCallback; private final List mRequestList; - private final Executor mExecutor; + private final Handler mHandler; private final int mSessionId; /** *

Determine if the callback holder is for a constrained high speed request list that @@ -1379,13 +1366,13 @@ public class CameraDeviceImpl extends CameraDevice private final boolean mHasBatchedOutputs; CaptureCallbackHolder(CaptureCallback callback, List requestList, - Executor executor, boolean repeating, int sessionId) { - if (callback == null || executor == null) { + Handler handler, boolean repeating, int sessionId) { + if (callback == null || handler == null) { throw new UnsupportedOperationException( "Must have a valid handler and a valid callback"); } mRepeating = repeating; - mExecutor = executor; + mHandler = handler; mRequestList = new ArrayList(requestList); mCallback = callback; mSessionId = sessionId; @@ -1438,8 +1425,8 @@ public class CameraDeviceImpl extends CameraDevice return getRequest(0); } - public Executor getExecutor() { - return mExecutor; + public Handler getHandler() { + return mHandler; } public int getSessionId() { @@ -1823,12 +1810,7 @@ public class CameraDeviceImpl extends CameraDevice } } }; - final long ident = Binder.clearCallingIdentity(); - try { - holder.getExecutor().execute(resultDispatch); - } finally { - Binder.restoreCallingIdentity(ident); - } + holder.getHandler().post(resultDispatch); } } } @@ -1879,7 +1861,7 @@ public class CameraDeviceImpl extends CameraDevice private void scheduleNotifyError(int code) { mInError = true; CameraDeviceImpl.this.mDeviceHandler.post(obtainRunnable( - CameraDeviceCallbacks::notifyError, this, code)); + CameraDeviceCallbacks::notifyError, this, code)); } private void notifyError(int code) { @@ -1947,41 +1929,36 @@ public class CameraDeviceImpl extends CameraDevice if (isClosed()) return; // Dispatch capture start notice - final long ident = Binder.clearCallingIdentity(); - try { - holder.getExecutor().execute( - new Runnable() { - @Override - public void run() { - if (!CameraDeviceImpl.this.isClosed()) { - final int subsequenceId = resultExtras.getSubsequenceId(); - final CaptureRequest request = holder.getRequest(subsequenceId); - - if (holder.hasBatchedOutputs()) { - // Send derived onCaptureStarted for requests within the - // batch - final Range fpsRange = - request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); - for (int i = 0; i < holder.getRequestCount(); i++) { - holder.getCallback().onCaptureStarted( - CameraDeviceImpl.this, - holder.getRequest(i), - timestamp - (subsequenceId - i) * - NANO_PER_SECOND/fpsRange.getUpper(), - frameNumber - (subsequenceId - i)); - } - } else { + holder.getHandler().post( + new Runnable() { + @Override + public void run() { + if (!CameraDeviceImpl.this.isClosed()) { + final int subsequenceId = resultExtras.getSubsequenceId(); + final CaptureRequest request = holder.getRequest(subsequenceId); + + if (holder.hasBatchedOutputs()) { + // Send derived onCaptureStarted for requests within the batch + final Range fpsRange = + request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); + for (int i = 0; i < holder.getRequestCount(); i++) { holder.getCallback().onCaptureStarted( CameraDeviceImpl.this, - holder.getRequest(resultExtras.getSubsequenceId()), - timestamp, frameNumber); + holder.getRequest(i), + timestamp - (subsequenceId - i) * + NANO_PER_SECOND/fpsRange.getUpper(), + frameNumber - (subsequenceId - i)); } + } else { + holder.getCallback().onCaptureStarted( + CameraDeviceImpl.this, + holder.getRequest(resultExtras.getSubsequenceId()), + timestamp, frameNumber); } } - }); - } finally { - Binder.restoreCallingIdentity(ident); - } + } + }); + } } @@ -2134,12 +2111,7 @@ public class CameraDeviceImpl extends CameraDevice finalResult = resultAsCapture; } - final long ident = Binder.clearCallingIdentity(); - try { - holder.getExecutor().execute(resultDispatch); - } finally { - Binder.restoreCallingIdentity(ident); - } + holder.getHandler().post(resultDispatch); // Collect the partials for a total result; or mark the frame as totally completed mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult, @@ -2235,12 +2207,7 @@ public class CameraDeviceImpl extends CameraDevice } }; // Dispatch the failure callback - final long ident = Binder.clearCallingIdentity(); - try { - holder.getExecutor().execute(failureDispatch); - } finally { - Binder.restoreCallingIdentity(ident); - } + holder.getHandler().post(failureDispatch); } } else { boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT); @@ -2280,12 +2247,7 @@ public class CameraDeviceImpl extends CameraDevice checkAndFireSequenceComplete(); // Dispatch the failure callback - final long ident = Binder.clearCallingIdentity(); - try { - holder.getExecutor().execute(failureDispatch); - } finally { - Binder.restoreCallingIdentity(ident); - } + holder.getHandler().post(failureDispatch); } } @@ -2293,37 +2255,6 @@ public class CameraDeviceImpl extends CameraDevice } // public class CameraDeviceCallbacks /** - * Instantiate a new Executor. - * - *

If the callback isn't null, check the handler and instantiate a new executor, - * otherwise instantiate a new executor in case handler is valid.

- */ - static Executor getExecutor(Handler handler, T callback) { - if (callback != null) { - return checkAndWrapHandler(handler); - } - - if (handler != null) { - return new HandlerExecutor(handler); - } - - return null; - } - - /** - * Wrap Handler in Executor. - * - *

- * If handler is null, get the current thread's - * Looper to create a Executor with. If no looper exists, throw - * {@code IllegalArgumentException}. - *

- */ - static Executor checkAndWrapHandler(Handler handler) { - return new HandlerExecutor(checkHandler(handler)); - } - - /** * Default handler management. * *

diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java index 7bdb4a2f1339..a79a6c17f925 100644 --- a/core/java/android/hardware/camera2/params/SessionConfiguration.java +++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java @@ -17,10 +17,10 @@ package android.hardware.camera2.params; -import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.IntDef; +import android.os.Handler; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; @@ -31,7 +31,6 @@ import android.hardware.camera2.params.OutputConfiguration; import java.util.Collections; import java.util.List; import java.util.ArrayList; -import java.util.concurrent.Executor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -79,7 +78,7 @@ public final class SessionConfiguration { private List mOutputConfigurations; private CameraCaptureSession.StateCallback mStateCallback; private int mSessionType; - private Executor mExecutor = null; + private Handler mHandler = null; private InputConfiguration mInputConfig = null; private CaptureRequest mSessionParameters = null; @@ -88,9 +87,10 @@ public final class SessionConfiguration { * * @param sessionType The session type. * @param outputs A list of output configurations for the capture session. - * @param executor The executor which should be used to invoke the callback. In general it is - * recommended that camera operations are not done on the main (UI) thread. * @param cb A state callback interface implementation. + * @param handler The handler on which the callback will be invoked. If it is + * set to null, the callback will be invoked on the current thread's + * {@link android.os.Looper looper}. * * @see #SESSION_REGULAR * @see #SESSION_HIGH_SPEED @@ -101,12 +101,11 @@ public final class SessionConfiguration { */ public SessionConfiguration(@SessionMode int sessionType, @NonNull List outputs, - @NonNull @CallbackExecutor Executor executor, - @NonNull CameraCaptureSession.StateCallback cb) { + @NonNull CameraCaptureSession.StateCallback cb, @Nullable Handler handler) { mSessionType = sessionType; mOutputConfigurations = Collections.unmodifiableList(new ArrayList<>(outputs)); mStateCallback = cb; - mExecutor = executor; + mHandler = handler; } /** @@ -137,12 +136,14 @@ public final class SessionConfiguration { } /** - * Retrieve the {@link java.util.concurrent.Executor} for the capture session. + * Retrieve the {@link CameraCaptureSession.StateCallback} for the capture session. * - * @return The Executor on which the callback will be invoked. + * @return The handler on which the callback will be invoked. If it is + * set to null, the callback will be invoked on the current thread's + * {@link android.os.Looper looper}. */ - public Executor getExecutor() { - return mExecutor; + public Handler getHandler() { + return mHandler; } /** diff --git a/core/java/android/hardware/camera2/utils/TaskDrainer.java b/core/java/android/hardware/camera2/utils/TaskDrainer.java index e71f26a18792..ed30ff34afc8 100644 --- a/core/java/android/hardware/camera2/utils/TaskDrainer.java +++ b/core/java/android/hardware/camera2/utils/TaskDrainer.java @@ -15,11 +15,11 @@ */ package android.hardware.camera2.utils; +import android.os.Handler; import android.util.Log; import java.util.HashSet; import java.util.Set; -import java.util.concurrent.Executor; import static com.android.internal.util.Preconditions.*; @@ -55,7 +55,7 @@ public class TaskDrainer { private static final String TAG = "TaskDrainer"; private final boolean DEBUG = false; - private final Executor mExecutor; + private final Handler mHandler; private final DrainListener mListener; private final String mName; @@ -73,27 +73,28 @@ public class TaskDrainer { /** * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener - * via the {@code executor}. + * via the {@code handler}. * - * @param executor a non-{@code null} executor to use for listener execution + * @param handler a non-{@code null} handler to use to post runnables to * @param listener a non-{@code null} listener where {@code onDrained} will be called */ - public TaskDrainer(Executor executor, DrainListener listener) { - mExecutor = checkNotNull(executor, "executor must not be null"); + public TaskDrainer(Handler handler, DrainListener listener) { + mHandler = checkNotNull(handler, "handler must not be null"); mListener = checkNotNull(listener, "listener must not be null"); mName = null; } /** * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener - * via the {@code executor}. + * via the {@code handler}. * - * @param executor a non-{@code null} executor to use for listener execution + * @param handler a non-{@code null} handler to use to post runnables to * @param listener a non-{@code null} listener where {@code onDrained} will be called * @param name an optional name used for debug logging */ - public TaskDrainer(Executor executor, DrainListener listener, String name) { - mExecutor = checkNotNull(executor, "executor must not be null"); + public TaskDrainer(Handler handler, DrainListener listener, String name) { + // XX: Probably don't need a handler at all here + mHandler = checkNotNull(handler, "handler must not be null"); mListener = checkNotNull(listener, "listener must not be null"); mName = name; } @@ -199,12 +200,15 @@ public class TaskDrainer { } private void postDrained() { - mExecutor.execute(() -> { + mHandler.post(new Runnable() { + @Override + public void run() { if (DEBUG) { Log.v(TAG + "[" + mName + "]", "onDrained"); } mListener.onDrained(); + } }); } } diff --git a/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java b/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java index 9615450be447..f6272c9e6a66 100644 --- a/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java +++ b/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java @@ -16,8 +16,7 @@ package android.hardware.camera2.utils; import android.hardware.camera2.utils.TaskDrainer.DrainListener; - -import java.util.concurrent.Executor; +import android.os.Handler; /** * Keep track of a single concurrent task starting and finishing; @@ -39,25 +38,25 @@ public class TaskSingleDrainer { /** * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener - * via the {@code executor}. + * via the {@code handler}. * - * @param executor a non-{@code null} executor to use for listener execution + * @param handler a non-{@code null} handler to use to post runnables to * @param listener a non-{@code null} listener where {@code onDrained} will be called */ - public TaskSingleDrainer(Executor executor, DrainListener listener) { - mTaskDrainer = new TaskDrainer<>(executor, listener); + public TaskSingleDrainer(Handler handler, DrainListener listener) { + mTaskDrainer = new TaskDrainer<>(handler, listener); } /** * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener - * via the {@code executor}. + * via the {@code handler}. * - * @param executor a non-{@code null} executor to use for listener execution + * @param handler a non-{@code null} handler to use to post runnables to * @param listener a non-{@code null} listener where {@code onDrained} will be called * @param name an optional name used for debug logging */ - public TaskSingleDrainer(Executor executor, DrainListener listener, String name) { - mTaskDrainer = new TaskDrainer<>(executor, listener, name); + public TaskSingleDrainer(Handler handler, DrainListener listener, String name) { + mTaskDrainer = new TaskDrainer<>(handler, listener, name); } /** -- 2.11.0