diff options
18 files changed, 309 insertions, 812 deletions
diff --git a/api/current.txt b/api/current.txt index 8efc7044af5f..dc0ff919cdc7 100644 --- a/api/current.txt +++ b/api/current.txt @@ -16434,8 +16434,8 @@ package android.hardware.camera2.params { } public final class SessionConfiguration { - ctor public SessionConfiguration(int, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler); - method public android.os.Handler getHandler(); + ctor public SessionConfiguration(int, java.util.List<android.hardware.camera2.params.OutputConfiguration>, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.StateCallback); + method public java.util.concurrent.Executor getExecutor(); method public android.hardware.camera2.params.InputConfiguration getInputConfiguration(); method public java.util.List<android.hardware.camera2.params.OutputConfiguration> 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 72db33f90c20..f47d4640c09c 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -819,7 +819,8 @@ 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. + * configurations are empty; or the session configuration + * executor is invalid. * @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 deleted file mode 100644 index 866f370f5d5d..000000000000 --- a/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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. - * - * <p>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}.</p> - * - * @param <T> - * source dispatch type, whose methods with {@link #dispatch} will be called - * @param <TArg> - * argument replacement type, args in {@link #dispatch} matching {@code argumentIndex} - * will be overriden to objects of this type - */ -public class ArgumentReplacingDispatcher<T, TArg> implements Dispatchable<T> { - - private final Dispatchable<T> 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. - * - * <p>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}.</p> - * - * <p>If a method dispatched has less arguments than {@code argumentIndex}, it is - * passed through with the arguments unchanged.</p> - * - * @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<T> 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 deleted file mode 100644 index fe575b277616..000000000000 --- a/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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. - * - * <p>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.</p> - * - * <p>This enables a single listener to be converted into a multi-listener.</p> - */ -public class BroadcastDispatcher<T> implements Dispatchable<T> { - - private final List<Dispatchable<T>> mDispatchTargets; - - /** - * Create a broadcast dispatcher from the supplied dispatch targets. - * - * @param dispatchTargets one or more targets to dispatch to - */ - @SafeVarargs - public BroadcastDispatcher(Dispatchable<T>... 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<T> 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 deleted file mode 100644 index 753103f45ea8..000000000000 --- a/core/java/android/hardware/camera2/dispatch/Dispatchable.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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. - * - * <p>This can be used to intercept method calls and do work around them, redirect work, - * or block calls entirely.</p> - */ -public interface Dispatchable<T> { - /** - * 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 deleted file mode 100644 index 75f97e4656aa..000000000000 --- a/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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. - * - * <p>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.</p> - * - * @param <TFrom> source dispatch type, whose methods with {@link #dispatch} will be called - * @param <T> destination dispatch type, methods will be converted to the class of {@code T} - */ -public class DuckTypingDispatcher<TFrom, T> implements Dispatchable<TFrom> { - - private final MethodNameInvoker<T> 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<T> target, Class<T> targetClass) { - checkNotNull(targetClass, "targetClass must not be null"); - checkNotNull(target, "target must not be null"); - - mDuck = new MethodNameInvoker<T>(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 deleted file mode 100644 index f8e9d49a95c6..000000000000 --- a/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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}. - * - * <p>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.</p> - * - * <p>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).</p> - */ -public class HandlerDispatcher<T> implements Dispatchable<T> { - - private static final String TAG = "HandlerDispatcher"; - - private final Dispatchable<T> 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 <T> 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<T> 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 deleted file mode 100644 index ac5f52676df4..000000000000 --- a/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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<T> implements Dispatchable<T> { - - 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 deleted file mode 100644 index 8296b7a915a4..000000000000 --- a/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 <T> destination dispatch type, methods will be looked up in the class of {@code T} - */ -public class MethodNameInvoker<T> { - - private final Dispatchable<T> mTarget; - private final Class<T> mTargetClass; - private final Method[] mTargetClassMethods; - private final ConcurrentHashMap<String, Method> 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<T> target, Class<T> targetClass) { - mTargetClass = targetClass; - mTargetClassMethods = targetClass.getMethods(); - mTarget = target; - } - - /** - * Invoke a method by its name. - * - * <p>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.</p> - * - * @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> 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 deleted file mode 100644 index fada075cba67..000000000000 --- a/core/java/android/hardware/camera2/dispatch/NullDispatcher.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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<T> implements Dispatchable<T> { - /** - * 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 deleted file mode 100644 index 783d0a1b54d5..000000000000 --- a/core/java/android/hardware/camera2/dispatch/package.html +++ /dev/null @@ -1,3 +0,0 @@ -<body> -{@hide} -</body> diff --git a/core/java/android/hardware/camera2/impl/CallbackProxies.java b/core/java/android/hardware/camera2/impl/CallbackProxies.java index c9eecf10e3d3..9e4cb80b89f0 100644 --- a/core/java/android/hardware/camera2/impl/CallbackProxies.java +++ b/core/java/android/hardware/camera2/impl/CallbackProxies.java @@ -15,16 +15,17 @@ */ 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.*; /** @@ -34,164 +35,86 @@ import static com.android.internal.util.Preconditions.*; * to use our own proxy mechanism.</p> */ public class CallbackProxies { - - // TODO: replace with codegen - - public static class DeviceStateCallbackProxy extends CameraDeviceImpl.StateCallbackKK { - private final MethodNameInvoker<CameraDeviceImpl.StateCallbackKK> mProxy; - - public DeviceStateCallbackProxy( - Dispatchable<CameraDeviceImpl.StateCallbackKK> 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<CameraDeviceImpl.CaptureCallback> mProxy; - - public DeviceCaptureCallbackProxy( - Dispatchable<CameraDeviceImpl.CaptureCallback> 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 MethodNameInvoker<CameraCaptureSession.StateCallback> mProxy; + private final Executor mExecutor; + private final CameraCaptureSession.StateCallback mCallback; - public SessionStateCallbackProxy( - Dispatchable<CameraCaptureSession.StateCallback> dispatchTarget) { - dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null"); - mProxy = new MethodNameInvoker<>(dispatchTarget, - CameraCaptureSession.StateCallback.class); + public SessionStateCallbackProxy(Executor executor, + CameraCaptureSession.StateCallback callback) { + mExecutor = checkNotNull(executor, "executor must not be null"); + mCallback = checkNotNull(callback, "callback must not be null"); } @Override public void onConfigured(CameraCaptureSession session) { - mProxy.invoke("onConfigured", session); + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onConfigured(session)); + } finally { + Binder.restoreCallingIdentity(ident); + } } @Override public void onConfigureFailed(CameraCaptureSession session) { - mProxy.invoke("onConfigureFailed", session); + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onConfigureFailed(session)); + } finally { + Binder.restoreCallingIdentity(ident); + } } @Override public void onReady(CameraCaptureSession session) { - mProxy.invoke("onReady", session); + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onReady(session)); + } finally { + Binder.restoreCallingIdentity(ident); + } } @Override public void onActive(CameraCaptureSession session) { - mProxy.invoke("onActive", session); + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onActive(session)); + } finally { + Binder.restoreCallingIdentity(ident); + } } @Override public void onCaptureQueueEmpty(CameraCaptureSession session) { - mProxy.invoke("onCaptureQueueEmpty", session); + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onCaptureQueueEmpty(session)); + } finally { + Binder.restoreCallingIdentity(ident); + } } @Override public void onClosed(CameraCaptureSession session) { - mProxy.invoke("onClosed", session); + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onClosed(session)); + } finally { + Binder.restoreCallingIdentity(ident); + } } @Override public void onSurfacePrepared(CameraCaptureSession session, Surface surface) { - mProxy.invoke("onSurfacePrepared", session, surface); + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onSurfacePrepared(session, surface)); + } finally { + Binder.restoreCallingIdentity(ident); + } } } diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index 8b8bbc34f7d2..f3f6c84a7e0b 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -20,20 +20,18 @@ 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.*; @@ -51,11 +49,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 {@link Handler#post(Runnable) posted} to {@code mStateHandler}. + * automatically invoked via {@code mStateExecutor}. */ private final CameraCaptureSession.StateCallback mStateCallback; - /** User-specified state handler used for outgoing state callback events */ - private final Handler mStateHandler; + /** User-specified state executor used for outgoing state callback events */ + private final Executor mStateExecutor; /** Internal camera device; used to translate calls into existing deprecated API */ private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl; @@ -87,7 +85,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession * (e.g. no pending captures, no repeating requests, no flush).</p> */ CameraCaptureSessionImpl(int id, Surface input, - CameraCaptureSession.StateCallback callback, Handler stateHandler, + CameraCaptureSession.StateCallback callback, Executor stateExecutor, android.hardware.camera2.impl.CameraDeviceImpl deviceImpl, Handler deviceStateHandler, boolean configureSuccess) { if (callback == null) { @@ -98,8 +96,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession mIdString = String.format("Session %d: ", mId); mInput = input; - mStateHandler = checkHandler(stateHandler); - mStateCallback = createUserStateCallbackProxy(mStateHandler, callback); + mStateExecutor = checkNotNull(stateExecutor, "stateExecutor must not be null"); + mStateCallback = createUserStateCallbackProxy(mStateExecutor, callback); mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null"); mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null"); @@ -110,12 +108,12 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession * This ensures total ordering between CameraDevice.StateCallback and * CameraDeviceImpl.CaptureCallback events. */ - mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(), - /*name*/"seq"); - mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(), - /*name*/"idle"); - mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(), - /*name*/"abort"); + 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"); // CameraDevice should call configureOutputs and have it finish before constructing us @@ -446,114 +444,140 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession } /** - * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code handler}. + * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code executor}. */ - private StateCallback createUserStateCallbackProxy(Handler handler, StateCallback callback) { - InvokeDispatcher<StateCallback> userCallbackSink = new InvokeDispatcher<>(callback); - HandlerDispatcher<StateCallback> handlerPassthrough = - new HandlerDispatcher<>(userCallbackSink, handler); - - return new CallbackProxies.SessionStateCallbackProxy(handlerPassthrough); + private StateCallback createUserStateCallbackProxy(Executor executor, StateCallback callback) { + return new CallbackProxies.SessionStateCallbackProxy(executor, callback); } /** * Forward callbacks from * CameraDeviceImpl.CaptureCallback to the CameraCaptureSession.CaptureCallback. * - * <p>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).</p> - * * <p>When a capture sequence finishes, update the pending checked sequences set.</p> */ @SuppressWarnings("deprecation") private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy( Handler handler, CaptureCallback callback) { - CameraDeviceImpl.CaptureCallback localCallback = new CameraDeviceImpl.CaptureCallback() { + final Executor executor = (callback != null) ? CameraDeviceImpl.checkAndWrapHandler( + handler) : null; + return new CameraDeviceImpl.CaptureCallback() { @Override public void onCaptureStarted(CameraDevice camera, CaptureRequest request, long timestamp, long frameNumber) { - // Do nothing + if ((callback != null) && (executor != null)) { + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.onCaptureStarted( + CameraCaptureSessionImpl.this, request, timestamp, + frameNumber)); + } finally { + Binder.restoreCallingIdentity(ident); + } + } } @Override public void onCapturePartial(CameraDevice camera, CaptureRequest request, android.hardware.camera2.CaptureResult result) { - // Do nothing + if ((callback != null) && (executor != null)) { + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.onCapturePartial( + CameraCaptureSessionImpl.this, request, result)); + } finally { + Binder.restoreCallingIdentity(ident); + } + } } @Override public void onCaptureProgressed(CameraDevice camera, CaptureRequest request, android.hardware.camera2.CaptureResult partialResult) { - // Do nothing + if ((callback != null) && (executor != null)) { + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.onCaptureProgressed( + CameraCaptureSessionImpl.this, request, partialResult)); + } finally { + Binder.restoreCallingIdentity(ident); + } + } } @Override public void onCaptureCompleted(CameraDevice camera, CaptureRequest request, android.hardware.camera2.TotalCaptureResult result) { - // Do nothing + if ((callback != null) && (executor != null)) { + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.onCaptureCompleted( + CameraCaptureSessionImpl.this, request, result)); + } finally { + Binder.restoreCallingIdentity(ident); + } + } } @Override public void onCaptureFailed(CameraDevice camera, CaptureRequest request, android.hardware.camera2.CaptureFailure failure) { - // Do nothing + if ((callback != null) && (executor != null)) { + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.onCaptureFailed( + CameraCaptureSessionImpl.this, request, failure)); + } finally { + Binder.restoreCallingIdentity(ident); + } + } } @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) { - // Do nothing + if ((callback != null) && (executor != null)) { + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.onCaptureBufferLost( + CameraCaptureSessionImpl.this, request, target, frameNumber)); + } finally { + Binder.restoreCallingIdentity(ident); + } + } } - }; - - /* - * 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<CameraDeviceImpl.CaptureCallback> localSink = - new InvokeDispatcher<>(localCallback); - - InvokeDispatcher<CaptureCallback> userCallbackSink = - new InvokeDispatcher<>(callback); - HandlerDispatcher<CaptureCallback> handlerPassthrough = - new HandlerDispatcher<>(userCallbackSink, handler); - DuckTypingDispatcher<CameraDeviceImpl.CaptureCallback, CaptureCallback> duckToSession - = new DuckTypingDispatcher<>(handlerPassthrough, CaptureCallback.class); - ArgumentReplacingDispatcher<CameraDeviceImpl.CaptureCallback, CameraCaptureSessionImpl> - replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession, - /*argumentIndex*/0, this); - - BroadcastDispatcher<CameraDeviceImpl.CaptureCallback> broadcaster = - new BroadcastDispatcher<CameraDeviceImpl.CaptureCallback>( - 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 06c2c25ab6bb..4ee08bae84cb 100644 --- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java @@ -33,6 +33,7 @@ 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.*; @@ -59,14 +60,14 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl * (e.g. no pending captures, no repeating requests, no flush).</p> */ CameraConstrainedHighSpeedCaptureSessionImpl(int id, - CameraCaptureSession.StateCallback callback, Handler stateHandler, + CameraCaptureSession.StateCallback callback, Executor stateExecutor, 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, - stateHandler, deviceImpl, deviceStateHandler, configureSuccess); + stateExecutor, 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 511fa43a5441..b328bb1726e9 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -35,8 +35,10 @@ 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; @@ -58,6 +60,7 @@ 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 @@ -501,8 +504,9 @@ public class CameraDeviceImpl extends CameraDevice for (Surface surface : outputs) { outConfigurations.add(new OutputConfiguration(surface)); } - createCaptureSessionInternal(null, outConfigurations, callback, handler, - /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null); + createCaptureSessionInternal(null, outConfigurations, callback, + checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, + /*sessionParams*/ null); } @Override @@ -517,7 +521,7 @@ public class CameraDeviceImpl extends CameraDevice // OutputConfiguration objects are immutable, but need to have our own array List<OutputConfiguration> currentOutputs = new ArrayList<>(outputConfigurations); - createCaptureSessionInternal(null, currentOutputs, callback, handler, + createCaptureSessionInternal(null, currentOutputs, callback, checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/null); } @@ -537,8 +541,9 @@ public class CameraDeviceImpl extends CameraDevice for (Surface surface : outputs) { outConfigurations.add(new OutputConfiguration(surface)); } - createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler, - /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null); + createCaptureSessionInternal(inputConfig, outConfigurations, callback, + checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, + /*sessionParams*/ null); } @Override @@ -566,8 +571,8 @@ public class CameraDeviceImpl extends CameraDevice currentOutputs.add(new OutputConfiguration(output)); } createCaptureSessionInternal(inputConfig, currentOutputs, - callback, handler, /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, - /*sessionParams*/ null); + callback, checkAndWrapHandler(handler), + /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null); } @Override @@ -582,7 +587,8 @@ public class CameraDeviceImpl extends CameraDevice for (Surface surface : outputs) { outConfigurations.add(new OutputConfiguration(surface)); } - createCaptureSessionInternal(null, outConfigurations, callback, handler, + createCaptureSessionInternal(null, outConfigurations, callback, + checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE, /*sessionParams*/ null); } @@ -597,8 +603,8 @@ public class CameraDeviceImpl extends CameraDevice for (OutputConfiguration output : outputs) { currentOutputs.add(new OutputConfiguration(output)); } - createCaptureSessionInternal(inputConfig, currentOutputs, callback, handler, operatingMode, - /*sessionParams*/ null); + createCaptureSessionInternal(inputConfig, currentOutputs, callback, + checkAndWrapHandler(handler), operatingMode, /*sessionParams*/ null); } @Override @@ -612,14 +618,17 @@ 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.getHandler(), config.getSessionType(), + config.getStateCallback(), config.getExecutor(), config.getSessionType(), config.getSessionParameters()); } private void createCaptureSessionInternal(InputConfiguration inputConfig, List<OutputConfiguration> outputConfigurations, - CameraCaptureSession.StateCallback callback, Handler handler, + CameraCaptureSession.StateCallback callback, Executor executor, int operatingMode, CaptureRequest sessionParams) throws CameraAccessException { synchronized(mInterfaceLock) { if (DEBUG) { @@ -673,12 +682,11 @@ public class CameraDeviceImpl extends CameraDevice SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config); newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++, - callback, handler, this, mDeviceHandler, configureSuccess, + callback, executor, this, mDeviceHandler, configureSuccess, mCharacteristics); } else { newSession = new CameraCaptureSessionImpl(mNextSessionId++, input, - callback, handler, this, mDeviceHandler, - configureSuccess); + callback, executor, this, mDeviceHandler, configureSuccess); } // TODO: wait until current session closes, then create the new session @@ -963,7 +971,12 @@ public class CameraDeviceImpl extends CameraDevice } } }; - holder.getHandler().post(resultDispatch); + final long ident = Binder.clearCallingIdentity(); + try { + holder.getExecutor().execute(resultDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } } else { Log.w(TAG, String.format( "did not register callback to request %d", @@ -984,9 +997,9 @@ public class CameraDeviceImpl extends CameraDevice private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, Handler handler, boolean repeating) throws CameraAccessException { - // Need a valid handler, or current thread needs to have a looper, if + // Need a valid executor, or current thread needs to have a looper, if // callback is valid - handler = checkHandler(handler, callback); + Executor executor = getExecutor(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 @@ -1040,7 +1053,7 @@ public class CameraDeviceImpl extends CameraDevice if (callback != null) { mCaptureCallbackMap.put(requestInfo.getRequestId(), new CaptureCallbackHolder( - callback, requestList, handler, repeating, mNextSessionId - 1)); + callback, requestList, executor, repeating, mNextSessionId - 1)); } else { if (DEBUG) { Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null"); @@ -1354,7 +1367,7 @@ public class CameraDeviceImpl extends CameraDevice private final boolean mRepeating; private final CaptureCallback mCallback; private final List<CaptureRequest> mRequestList; - private final Handler mHandler; + private final Executor mExecutor; private final int mSessionId; /** * <p>Determine if the callback holder is for a constrained high speed request list that @@ -1366,13 +1379,13 @@ public class CameraDeviceImpl extends CameraDevice private final boolean mHasBatchedOutputs; CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList, - Handler handler, boolean repeating, int sessionId) { - if (callback == null || handler == null) { + Executor executor, boolean repeating, int sessionId) { + if (callback == null || executor == null) { throw new UnsupportedOperationException( "Must have a valid handler and a valid callback"); } mRepeating = repeating; - mHandler = handler; + mExecutor = executor; mRequestList = new ArrayList<CaptureRequest>(requestList); mCallback = callback; mSessionId = sessionId; @@ -1425,8 +1438,8 @@ public class CameraDeviceImpl extends CameraDevice return getRequest(0); } - public Handler getHandler() { - return mHandler; + public Executor getExecutor() { + return mExecutor; } public int getSessionId() { @@ -1810,7 +1823,12 @@ public class CameraDeviceImpl extends CameraDevice } } }; - holder.getHandler().post(resultDispatch); + final long ident = Binder.clearCallingIdentity(); + try { + holder.getExecutor().execute(resultDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } } } } @@ -1861,7 +1879,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) { @@ -1929,36 +1947,41 @@ public class CameraDeviceImpl extends CameraDevice if (isClosed()) return; // Dispatch capture start notice - 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<Integer> fpsRange = - request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); - for (int i = 0; i < holder.getRequestCount(); i++) { + 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<Integer> 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.getCallback().onCaptureStarted( CameraDeviceImpl.this, - holder.getRequest(i), - timestamp - (subsequenceId - i) * - NANO_PER_SECOND/fpsRange.getUpper(), - frameNumber - (subsequenceId - i)); + holder.getRequest(resultExtras.getSubsequenceId()), + timestamp, frameNumber); } - } else { - holder.getCallback().onCaptureStarted( - CameraDeviceImpl.this, - holder.getRequest(resultExtras.getSubsequenceId()), - timestamp, frameNumber); } } - } - }); - + }); + } finally { + Binder.restoreCallingIdentity(ident); + } } } @@ -2111,7 +2134,12 @@ public class CameraDeviceImpl extends CameraDevice finalResult = resultAsCapture; } - holder.getHandler().post(resultDispatch); + final long ident = Binder.clearCallingIdentity(); + try { + holder.getExecutor().execute(resultDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } // Collect the partials for a total result; or mark the frame as totally completed mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult, @@ -2207,7 +2235,12 @@ public class CameraDeviceImpl extends CameraDevice } }; // Dispatch the failure callback - holder.getHandler().post(failureDispatch); + final long ident = Binder.clearCallingIdentity(); + try { + holder.getExecutor().execute(failureDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } } } else { boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT); @@ -2247,7 +2280,12 @@ public class CameraDeviceImpl extends CameraDevice checkAndFireSequenceComplete(); // Dispatch the failure callback - holder.getHandler().post(failureDispatch); + final long ident = Binder.clearCallingIdentity(); + try { + holder.getExecutor().execute(failureDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } } } @@ -2255,6 +2293,37 @@ public class CameraDeviceImpl extends CameraDevice } // public class CameraDeviceCallbacks /** + * Instantiate a new Executor. + * + * <p>If the callback isn't null, check the handler and instantiate a new executor, + * otherwise instantiate a new executor in case handler is valid.</p> + */ + static <T> 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. + * + * <p> + * If handler is null, get the current thread's + * Looper to create a Executor with. If no looper exists, throw + * {@code IllegalArgumentException}. + * </p> + */ + static Executor checkAndWrapHandler(Handler handler) { + return new HandlerExecutor(checkHandler(handler)); + } + + /** * Default handler management. * * <p> diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java index a79a6c17f925..7bdb4a2f1339 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,6 +31,7 @@ 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; @@ -78,7 +79,7 @@ public final class SessionConfiguration { private List<OutputConfiguration> mOutputConfigurations; private CameraCaptureSession.StateCallback mStateCallback; private int mSessionType; - private Handler mHandler = null; + private Executor mExecutor = null; private InputConfiguration mInputConfig = null; private CaptureRequest mSessionParameters = null; @@ -87,10 +88,9 @@ 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,11 +101,12 @@ public final class SessionConfiguration { */ public SessionConfiguration(@SessionMode int sessionType, @NonNull List<OutputConfiguration> outputs, - @NonNull CameraCaptureSession.StateCallback cb, @Nullable Handler handler) { + @NonNull @CallbackExecutor Executor executor, + @NonNull CameraCaptureSession.StateCallback cb) { mSessionType = sessionType; mOutputConfigurations = Collections.unmodifiableList(new ArrayList<>(outputs)); mStateCallback = cb; - mHandler = handler; + mExecutor = executor; } /** @@ -136,14 +137,12 @@ public final class SessionConfiguration { } /** - * Retrieve the {@link CameraCaptureSession.StateCallback} for the capture session. + * Retrieve the {@link java.util.concurrent.Executor} for the capture session. * - * @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}. + * @return The Executor on which the callback will be invoked. */ - public Handler getHandler() { - return mHandler; + public Executor getExecutor() { + return mExecutor; } /** diff --git a/core/java/android/hardware/camera2/utils/TaskDrainer.java b/core/java/android/hardware/camera2/utils/TaskDrainer.java index ed30ff34afc8..e71f26a18792 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<T> { private static final String TAG = "TaskDrainer"; private final boolean DEBUG = false; - private final Handler mHandler; + private final Executor mExecutor; private final DrainListener mListener; private final String mName; @@ -73,28 +73,27 @@ public class TaskDrainer<T> { /** * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener - * via the {@code handler}. + * via the {@code executor}. * - * @param handler a non-{@code null} handler to use to post runnables to + * @param executor a non-{@code null} executor to use for listener execution * @param listener a non-{@code null} listener where {@code onDrained} will be called */ - public TaskDrainer(Handler handler, DrainListener listener) { - mHandler = checkNotNull(handler, "handler must not be null"); + public TaskDrainer(Executor executor, DrainListener listener) { + mExecutor = checkNotNull(executor, "executor 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 handler}. + * via the {@code executor}. * - * @param handler a non-{@code null} handler to use to post runnables to + * @param executor a non-{@code null} executor to use for listener execution * @param listener a non-{@code null} listener where {@code onDrained} will be called * @param name an optional name used for debug logging */ - 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"); + public TaskDrainer(Executor executor, DrainListener listener, String name) { + mExecutor = checkNotNull(executor, "executor must not be null"); mListener = checkNotNull(listener, "listener must not be null"); mName = name; } @@ -200,15 +199,12 @@ public class TaskDrainer<T> { } private void postDrained() { - mHandler.post(new Runnable() { - @Override - public void run() { + mExecutor.execute(() -> { 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 f6272c9e6a66..9615450be447 100644 --- a/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java +++ b/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java @@ -16,7 +16,8 @@ package android.hardware.camera2.utils; import android.hardware.camera2.utils.TaskDrainer.DrainListener; -import android.os.Handler; + +import java.util.concurrent.Executor; /** * Keep track of a single concurrent task starting and finishing; @@ -38,25 +39,25 @@ public class TaskSingleDrainer { /** * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener - * via the {@code handler}. + * via the {@code executor}. * - * @param handler a non-{@code null} handler to use to post runnables to + * @param executor a non-{@code null} executor to use for listener execution * @param listener a non-{@code null} listener where {@code onDrained} will be called */ - public TaskSingleDrainer(Handler handler, DrainListener listener) { - mTaskDrainer = new TaskDrainer<>(handler, listener); + public TaskSingleDrainer(Executor executor, DrainListener listener) { + mTaskDrainer = new TaskDrainer<>(executor, listener); } /** * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener - * via the {@code handler}. + * via the {@code executor}. * - * @param handler a non-{@code null} handler to use to post runnables to + * @param executor a non-{@code null} executor to use for listener execution * @param listener a non-{@code null} listener where {@code onDrained} will be called * @param name an optional name used for debug logging */ - public TaskSingleDrainer(Handler handler, DrainListener listener, String name) { - mTaskDrainer = new TaskDrainer<>(handler, listener, name); + public TaskSingleDrainer(Executor executor, DrainListener listener, String name) { + mTaskDrainer = new TaskDrainer<>(executor, listener, name); } /** |