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:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
<body>
|
||||
{@hide}
|
||||
</body>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user