Camera: SessionConfiguration should use Executors

Handlers from clients should not be used any more.
Executors are the preferred method for invoking any
registered callbacks. Replace handlers as much as
possible with executors.

Bug: 73953366
Test: Camera CTS
Change-Id: I96aee1bc46e83dfb76a4c40c7f8ebbe18610788b
This commit is contained in:
Emilian Peev
2018-02-28 14:53:30 +00:00
parent 9bc5610869
commit 004e73c38c
18 changed files with 308 additions and 811 deletions

View File

@@ -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)

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -1,3 +0,0 @@
<body>
{@hide}
</body>

View File

@@ -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);
}
}
}

View File

@@ -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);
}
/**

View File

@@ -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

View File

@@ -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);
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++) {
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,13 +2280,49 @@ 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);
}
}
}
} // 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.
*

View File

@@ -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;
}
/**

View File

@@ -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();
}
});
}
}

View File

@@ -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);
}
/**