diff options
19 files changed, 483 insertions, 939 deletions
diff --git a/api/current.txt b/api/current.txt index 7b627a8fe03a..dc3ab4fdc1f6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -16449,8 +16449,8 @@ package android.hardware.camera2.params { } public final class SessionConfiguration { - ctor public SessionConfiguration(int, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler); - method public android.os.Handler getHandler(); + ctor public SessionConfiguration(int, java.util.List<android.hardware.camera2.params.OutputConfiguration>, java.util.concurrent.Executor, android.hardware.camera2.CameraCaptureSession.StateCallback); + method public java.util.concurrent.Executor getExecutor(); method public android.hardware.camera2.params.InputConfiguration getInputConfiguration(); method public java.util.List<android.hardware.camera2.params.OutputConfiguration> getOutputConfigurations(); method public android.hardware.camera2.CaptureRequest getSessionParameters(); diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 72db33f90c20..f47d4640c09c 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -819,7 +819,8 @@ public abstract class CameraDevice implements AutoCloseable { * @param config A session configuration (see {@link SessionConfiguration}). * * @throws IllegalArgumentException In case the session configuration is invalid; or the output - * configurations are empty. + * configurations are empty; or the session configuration + * executor is invalid. * @throws CameraAccessException In case the camera device is no longer connected or has * encountered a fatal error. * @see #createCaptureSession(List, CameraCaptureSession.StateCallback, Handler) diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index a2bc91e0cda6..4d642956b135 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -25,6 +25,7 @@ import android.hardware.CameraInfo; import android.hardware.CameraStatus; import android.hardware.ICameraService; import android.hardware.ICameraServiceListener; +import android.hardware.camera2.impl.CameraDeviceImpl; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.legacy.CameraDeviceUserShim; import android.hardware.camera2.legacy.LegacyMetadataMapper; @@ -41,6 +42,11 @@ import android.util.ArrayMap; import android.util.Log; import java.util.ArrayList; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; /** * <p>A system service manager for detecting, characterizing, and connecting to @@ -123,16 +129,8 @@ public final class CameraManager { */ public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback, @Nullable Handler handler) { - if (handler == null) { - Looper looper = Looper.myLooper(); - if (looper == null) { - throw new IllegalArgumentException( - "No handler given, and current thread has no looper!"); - } - handler = new Handler(looper); - } - - CameraManagerGlobal.get().registerAvailabilityCallback(callback, handler); + CameraManagerGlobal.get().registerAvailabilityCallback(callback, + CameraDeviceImpl.checkAndWrapHandler(handler)); } /** @@ -170,15 +168,8 @@ public final class CameraManager { * no looper. */ public void registerTorchCallback(@NonNull TorchCallback callback, @Nullable Handler handler) { - if (handler == null) { - Looper looper = Looper.myLooper(); - if (looper == null) { - throw new IllegalArgumentException( - "No handler given, and current thread has no looper!"); - } - handler = new Handler(looper); - } - CameraManagerGlobal.get().registerTorchCallback(callback, handler); + CameraManagerGlobal.get().registerTorchCallback(callback, + CameraDeviceImpl.checkAndWrapHandler(handler)); } /** @@ -728,12 +719,13 @@ public final class CameraManager { */ private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera"; + private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1); // Camera ID -> Status map private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>(); - // Registered availablility callbacks and their handlers - private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap = - new ArrayMap<AvailabilityCallback, Handler>(); + // Registered availablility callbacks and their executors + private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap = + new ArrayMap<AvailabilityCallback, Executor>(); // torch client binder to set the torch mode with. private Binder mTorchClientBinder = new Binder(); @@ -741,9 +733,9 @@ public final class CameraManager { // Camera ID -> Torch status map private final ArrayMap<String, Integer> mTorchStatus = new ArrayMap<String, Integer>(); - // Registered torch callbacks and their handlers - private final ArrayMap<TorchCallback, Handler> mTorchCallbackMap = - new ArrayMap<TorchCallback, Handler>(); + // Registered torch callbacks and their executors + private final ArrayMap<TorchCallback, Executor> mTorchCallbackMap = + new ArrayMap<TorchCallback, Executor>(); private final Object mLock = new Object(); @@ -925,49 +917,63 @@ public final class CameraManager { } } - private void postSingleUpdate(final AvailabilityCallback callback, final Handler handler, + private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor, final String id, final int status) { if (isAvailable(status)) { - handler.post( - new Runnable() { - @Override - public void run() { - callback.onCameraAvailable(id); - } - }); + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute( + new Runnable() { + @Override + public void run() { + callback.onCameraAvailable(id); + } + }); + } finally { + Binder.restoreCallingIdentity(ident); + } } else { - handler.post( - new Runnable() { - @Override - public void run() { - callback.onCameraUnavailable(id); - } - }); + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute( + new Runnable() { + @Override + public void run() { + callback.onCameraUnavailable(id); + } + }); + } finally { + Binder.restoreCallingIdentity(ident); + } } } - private void postSingleTorchUpdate(final TorchCallback callback, final Handler handler, + private void postSingleTorchUpdate(final TorchCallback callback, final Executor executor, final String id, final int status) { switch(status) { case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON: - case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: - handler.post( - new Runnable() { - @Override - public void run() { - callback.onTorchModeChanged(id, status == - ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON); - } + case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: { + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute(() -> { + callback.onTorchModeChanged(id, status == + ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON); }); + } finally { + Binder.restoreCallingIdentity(ident); + } + } break; - default: - handler.post( - new Runnable() { - @Override - public void run() { - callback.onTorchModeUnavailable(id); - } + default: { + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute(() -> { + callback.onTorchModeUnavailable(id); }); + } finally { + Binder.restoreCallingIdentity(ident); + } + } break; } } @@ -976,11 +982,11 @@ public final class CameraManager { * Send the state of all known cameras to the provided listener, to initialize * the listener's knowledge of camera state. */ - private void updateCallbackLocked(AvailabilityCallback callback, Handler handler) { + private void updateCallbackLocked(AvailabilityCallback callback, Executor executor) { for (int i = 0; i < mDeviceStatus.size(); i++) { String id = mDeviceStatus.keyAt(i); Integer status = mDeviceStatus.valueAt(i); - postSingleUpdate(callback, handler, id, status); + postSingleUpdate(callback, executor, id, status); } } @@ -1039,18 +1045,18 @@ public final class CameraManager { final int callbackCount = mCallbackMap.size(); for (int i = 0; i < callbackCount; i++) { - Handler handler = mCallbackMap.valueAt(i); + Executor executor = mCallbackMap.valueAt(i); final AvailabilityCallback callback = mCallbackMap.keyAt(i); - postSingleUpdate(callback, handler, id, status); + postSingleUpdate(callback, executor, id, status); } } // onStatusChangedLocked - private void updateTorchCallbackLocked(TorchCallback callback, Handler handler) { + private void updateTorchCallbackLocked(TorchCallback callback, Executor executor) { for (int i = 0; i < mTorchStatus.size(); i++) { String id = mTorchStatus.keyAt(i); Integer status = mTorchStatus.valueAt(i); - postSingleTorchUpdate(callback, handler, id, status); + postSingleTorchUpdate(callback, executor, id, status); } } @@ -1078,9 +1084,9 @@ public final class CameraManager { final int callbackCount = mTorchCallbackMap.size(); for (int i = 0; i < callbackCount; i++) { - final Handler handler = mTorchCallbackMap.valueAt(i); + final Executor executor = mTorchCallbackMap.valueAt(i); final TorchCallback callback = mTorchCallbackMap.keyAt(i); - postSingleTorchUpdate(callback, handler, id, status); + postSingleTorchUpdate(callback, executor, id, status); } } // onTorchStatusChangedLocked @@ -1089,16 +1095,16 @@ public final class CameraManager { * global listener singleton. * * @param callback the new callback to send camera availability notices to - * @param handler The handler on which the callback should be invoked. May not be null. + * @param executor The executor which should invoke the callback. May not be null. */ - public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) { + public void registerAvailabilityCallback(AvailabilityCallback callback, Executor executor) { synchronized (mLock) { connectCameraServiceLocked(); - Handler oldHandler = mCallbackMap.put(callback, handler); + Executor oldExecutor = mCallbackMap.put(callback, executor); // For new callbacks, provide initial availability information - if (oldHandler == null) { - updateCallbackLocked(callback, handler); + if (oldExecutor == null) { + updateCallbackLocked(callback, executor); } // If not connected to camera service, schedule a reconnect to camera service. @@ -1120,14 +1126,14 @@ public final class CameraManager { } } - public void registerTorchCallback(TorchCallback callback, Handler handler) { + public void registerTorchCallback(TorchCallback callback, Executor executor) { synchronized(mLock) { connectCameraServiceLocked(); - Handler oldHandler = mTorchCallbackMap.put(callback, handler); + Executor oldExecutor = mTorchCallbackMap.put(callback, executor); // For new callbacks, provide initial torch information - if (oldHandler == null) { - updateTorchCallbackLocked(callback, handler); + if (oldExecutor == null) { + updateTorchCallbackLocked(callback, executor); } // If not connected to camera service, schedule a reconnect to camera service. @@ -1165,13 +1171,7 @@ public final class CameraManager { * availability callback or torch status callback. */ private void scheduleCameraServiceReconnectionLocked() { - final Handler handler; - - if (mCallbackMap.size() > 0) { - handler = mCallbackMap.valueAt(0); - } else if (mTorchCallbackMap.size() > 0) { - handler = mTorchCallbackMap.valueAt(0); - } else { + if (mCallbackMap.isEmpty() && mTorchCallbackMap.isEmpty()) { // Not necessary to reconnect camera service if no client registers a callback. return; } @@ -1181,22 +1181,21 @@ public final class CameraManager { " ms"); } - handler.postDelayed( - new Runnable() { - @Override - public void run() { - ICameraService cameraService = getCameraService(); - if (cameraService == null) { - synchronized(mLock) { - if (DEBUG) { - Log.v(TAG, "Reconnecting Camera Service failed."); - } - scheduleCameraServiceReconnectionLocked(); - } + try { + mScheduler.schedule(() -> { + ICameraService cameraService = getCameraService(); + if (cameraService == null) { + synchronized(mLock) { + if (DEBUG) { + Log.v(TAG, "Reconnecting Camera Service failed."); } + scheduleCameraServiceReconnectionLocked(); } - }, - CAMERA_SERVICE_RECONNECT_DELAY_MS); + } + }, CAMERA_SERVICE_RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS); + } catch (RejectedExecutionException e) { + Log.e(TAG, "Failed to schedule camera service re-connect: " + e); + } } /** diff --git a/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java b/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java deleted file mode 100644 index 866f370f5d5d..000000000000 --- a/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.hardware.camera2.dispatch; - -import java.lang.reflect.Method; - -import static com.android.internal.util.Preconditions.*; - -/** - * A dispatcher that replaces one argument with another; replaces any argument at an index - * with another argument. - * - * <p>For example, we can override an {@code void onSomething(int x)} calls to have {@code x} always - * equal to 1. Or, if using this with a duck typing dispatcher, we could even overwrite {@code x} to - * be something - * that's not an {@code int}.</p> - * - * @param <T> - * source dispatch type, whose methods with {@link #dispatch} will be called - * @param <TArg> - * argument replacement type, args in {@link #dispatch} matching {@code argumentIndex} - * will be overriden to objects of this type - */ -public class ArgumentReplacingDispatcher<T, TArg> implements Dispatchable<T> { - - private final Dispatchable<T> mTarget; - private final int mArgumentIndex; - private final TArg mReplaceWith; - - /** - * Create a new argument replacing dispatcher; dispatches are forwarded to {@code target} - * after the argument is replaced. - * - * <p>For example, if a method {@code onAction(T1 a, Integer b, T2 c)} is invoked, and we wanted - * to replace all occurrences of {@code b} with {@code 0xDEADBEEF}, we would set - * {@code argumentIndex = 1} and {@code replaceWith = 0xDEADBEEF}.</p> - * - * <p>If a method dispatched has less arguments than {@code argumentIndex}, it is - * passed through with the arguments unchanged.</p> - * - * @param target destination dispatch type, methods will be redirected to this dispatcher - * @param argumentIndex the numeric index of the argument {@code >= 0} - * @param replaceWith arguments matching {@code argumentIndex} will be replaced with this object - */ - public ArgumentReplacingDispatcher(Dispatchable<T> target, int argumentIndex, - TArg replaceWith) { - mTarget = checkNotNull(target, "target must not be null"); - mArgumentIndex = checkArgumentNonnegative(argumentIndex, - "argumentIndex must not be negative"); - mReplaceWith = checkNotNull(replaceWith, "replaceWith must not be null"); - } - - @Override - public Object dispatch(Method method, Object[] args) throws Throwable { - - if (args.length > mArgumentIndex) { - args = arrayCopy(args); // don't change in-place since it can affect upstream dispatches - args[mArgumentIndex] = mReplaceWith; - } - - return mTarget.dispatch(method, args); - } - - private static Object[] arrayCopy(Object[] array) { - int length = array.length; - Object[] newArray = new Object[length]; - for (int i = 0; i < length; ++i) { - newArray[i] = array[i]; - } - return newArray; - } -} diff --git a/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java b/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java deleted file mode 100644 index fe575b277616..000000000000 --- a/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.hardware.camera2.dispatch; - - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; - -import static com.android.internal.util.Preconditions.*; - -/** - * Broadcast a single dispatch into multiple other dispatchables. - * - * <p>Every time {@link #dispatch} is invoked, all the broadcast targets will - * see the same dispatch as well. The first target's return value is returned.</p> - * - * <p>This enables a single listener to be converted into a multi-listener.</p> - */ -public class BroadcastDispatcher<T> implements Dispatchable<T> { - - private final List<Dispatchable<T>> mDispatchTargets; - - /** - * Create a broadcast dispatcher from the supplied dispatch targets. - * - * @param dispatchTargets one or more targets to dispatch to - */ - @SafeVarargs - public BroadcastDispatcher(Dispatchable<T>... dispatchTargets) { - mDispatchTargets = Arrays.asList( - checkNotNull(dispatchTargets, "dispatchTargets must not be null")); - } - - @Override - public Object dispatch(Method method, Object[] args) throws Throwable { - Object result = null; - boolean gotResult = false; - - for (Dispatchable<T> dispatchTarget : mDispatchTargets) { - Object localResult = dispatchTarget.dispatch(method, args); - - if (!gotResult) { - gotResult = true; - result = localResult; - } - } - - return result; - } -} diff --git a/core/java/android/hardware/camera2/dispatch/Dispatchable.java b/core/java/android/hardware/camera2/dispatch/Dispatchable.java deleted file mode 100644 index 753103f45ea8..000000000000 --- a/core/java/android/hardware/camera2/dispatch/Dispatchable.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.hardware.camera2.dispatch; - -import java.lang.reflect.Method; - -/** - * Dynamically dispatch a method and its argument to some object. - * - * <p>This can be used to intercept method calls and do work around them, redirect work, - * or block calls entirely.</p> - */ -public interface Dispatchable<T> { - /** - * Dispatch the method and arguments to this object. - * @param method a method defined in class {@code T} - * @param args arguments corresponding to said {@code method} - * @return the object returned when invoking {@code method} - * @throws Throwable any exception that might have been raised while invoking the method - */ - public Object dispatch(Method method, Object[] args) throws Throwable; -} diff --git a/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java b/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java deleted file mode 100644 index 75f97e4656aa..000000000000 --- a/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.hardware.camera2.dispatch; - - -import java.lang.reflect.Method; - -import static com.android.internal.util.Preconditions.*; - -/** - * Duck typing dispatcher; converts dispatch methods calls from one class to another by - * looking up equivalently methods at runtime by name. - * - * <p>For example, if two types have identical method names and arguments, but - * are not subclasses/subinterfaces of each other, this dispatcher will allow calls to be - * made from one type to the other.</p> - * - * @param <TFrom> source dispatch type, whose methods with {@link #dispatch} will be called - * @param <T> destination dispatch type, methods will be converted to the class of {@code T} - */ -public class DuckTypingDispatcher<TFrom, T> implements Dispatchable<TFrom> { - - private final MethodNameInvoker<T> mDuck; - - /** - * Create a new duck typing dispatcher. - * - * @param target destination dispatch type, methods will be redirected to this dispatcher - * @param targetClass destination dispatch class, methods will be converted to this class's - */ - public DuckTypingDispatcher(Dispatchable<T> target, Class<T> targetClass) { - checkNotNull(targetClass, "targetClass must not be null"); - checkNotNull(target, "target must not be null"); - - mDuck = new MethodNameInvoker<T>(target, targetClass); - } - - @Override - public Object dispatch(Method method, Object[] args) { - return mDuck.invoke(method.getName(), args); - } -} diff --git a/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java b/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java deleted file mode 100644 index f8e9d49a95c6..000000000000 --- a/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.hardware.camera2.dispatch; - -import android.hardware.camera2.utils.UncheckedThrow; -import android.os.Handler; -import android.util.Log; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import static com.android.internal.util.Preconditions.*; - -/** - * Forward all interface calls into a handler by posting it as a {@code Runnable}. - * - * <p>All calls will return immediately; functions with return values will return a default - * value of {@code null}, {@code 0}, or {@code false} where that value is legal.</p> - * - * <p>Any exceptions thrown on the handler while trying to invoke a method - * will be re-thrown. Throwing checked exceptions on a handler which doesn't expect any - * checked exceptions to be thrown will result in "undefined" behavior - * (although in practice it is usually thrown as normal).</p> - */ -public class HandlerDispatcher<T> implements Dispatchable<T> { - - private static final String TAG = "HandlerDispatcher"; - - private final Dispatchable<T> mDispatchTarget; - private final Handler mHandler; - - /** - * Create a dispatcher that forwards it's dispatch calls by posting - * them onto the {@code handler} as a {@code Runnable}. - * - * @param dispatchTarget the destination whose method calls will be redirected into the handler - * @param handler all calls into {@code dispatchTarget} will be posted onto this handler - * @param <T> the type of the element you want to wrap. - * @return a dispatcher that will forward it's dispatch calls to a handler - */ - public HandlerDispatcher(Dispatchable<T> dispatchTarget, Handler handler) { - mDispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null"); - mHandler = checkNotNull(handler, "handler must not be null"); - } - - @Override - public Object dispatch(final Method method, final Object[] args) throws Throwable { - mHandler.post(new Runnable() { - @Override - public void run() { - try { - mDispatchTarget.dispatch(method, args); - } catch (InvocationTargetException e) { - Throwable t = e.getTargetException(); - // Potential UB. Hopefully 't' is a runtime exception. - UncheckedThrow.throwAnyException(t); - } catch (IllegalAccessException e) { - // Impossible - Log.wtf(TAG, "IllegalAccessException while invoking " + method, e); - } catch (IllegalArgumentException e) { - // Impossible - Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e); - } catch (Throwable e) { - UncheckedThrow.throwAnyException(e); - } - } - }); - - // TODO handle primitive return values that would avoid NPE if unboxed - return null; - } -} diff --git a/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java b/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java deleted file mode 100644 index ac5f52676df4..000000000000 --- a/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.hardware.camera2.dispatch; - -import android.hardware.camera2.utils.UncheckedThrow; -import android.util.Log; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import static com.android.internal.util.Preconditions.*; - - -public class InvokeDispatcher<T> implements Dispatchable<T> { - - private static final String TAG = "InvocationSink"; - private final T mTarget; - - public InvokeDispatcher(T target) { - mTarget = checkNotNull(target, "target must not be null"); - } - - @Override - public Object dispatch(Method method, Object[] args) { - try { - return method.invoke(mTarget, args); - } catch (InvocationTargetException e) { - Throwable t = e.getTargetException(); - // Potential UB. Hopefully 't' is a runtime exception. - UncheckedThrow.throwAnyException(t); - } catch (IllegalAccessException e) { - // Impossible - Log.wtf(TAG, "IllegalAccessException while invoking " + method, e); - } catch (IllegalArgumentException e) { - // Impossible - Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e); - } - - // unreachable - return null; - } -} diff --git a/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java b/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java deleted file mode 100644 index 8296b7a915a4..000000000000 --- a/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.hardware.camera2.dispatch; - -import static com.android.internal.util.Preconditions.checkNotNull; - -import android.hardware.camera2.utils.UncheckedThrow; - -import java.lang.reflect.Method; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Invoke a method on a dispatchable by its name (without knowing the {@code Method} ahead of time). - * - * @param <T> destination dispatch type, methods will be looked up in the class of {@code T} - */ -public class MethodNameInvoker<T> { - - private final Dispatchable<T> mTarget; - private final Class<T> mTargetClass; - private final Method[] mTargetClassMethods; - private final ConcurrentHashMap<String, Method> mMethods = - new ConcurrentHashMap<>(); - - /** - * Create a new method name invoker. - * - * @param target destination dispatch type, invokes will be redirected to this dispatcher - * @param targetClass destination dispatch class, the invoked methods will be from this class - */ - public MethodNameInvoker(Dispatchable<T> target, Class<T> targetClass) { - mTargetClass = targetClass; - mTargetClassMethods = targetClass.getMethods(); - mTarget = target; - } - - /** - * Invoke a method by its name. - * - * <p>If more than one method exists in {@code targetClass}, the first method with the right - * number of arguments will be used, and later calls will all use that method.</p> - * - * @param methodName - * The name of the method, which will be matched 1:1 to the destination method - * @param params - * Variadic parameter list. - * @return - * The same kind of value that would normally be returned by calling {@code methodName} - * statically. - * - * @throws IllegalArgumentException if {@code methodName} does not exist on the target class - * @throws Throwable will rethrow anything that the target method would normally throw - */ - @SuppressWarnings("unchecked") - public <K> K invoke(String methodName, Object... params) { - checkNotNull(methodName, "methodName must not be null"); - - Method targetMethod = mMethods.get(methodName); - if (targetMethod == null) { - for (Method method : mTargetClassMethods) { - // TODO future: match types of params if possible - if (method.getName().equals(methodName) && - (params.length == method.getParameterTypes().length) ) { - targetMethod = method; - mMethods.put(methodName, targetMethod); - break; - } - } - - if (targetMethod == null) { - throw new IllegalArgumentException( - "Method " + methodName + " does not exist on class " + mTargetClass); - } - } - - try { - return (K) mTarget.dispatch(targetMethod, params); - } catch (Throwable e) { - UncheckedThrow.throwAnyException(e); - // unreachable - return null; - } - } -} diff --git a/core/java/android/hardware/camera2/dispatch/NullDispatcher.java b/core/java/android/hardware/camera2/dispatch/NullDispatcher.java deleted file mode 100644 index fada075cba67..000000000000 --- a/core/java/android/hardware/camera2/dispatch/NullDispatcher.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.hardware.camera2.dispatch; - - -import java.lang.reflect.Method; - -/** - * Do nothing when dispatching; follows the null object pattern. - */ -public class NullDispatcher<T> implements Dispatchable<T> { - /** - * Create a dispatcher that does nothing when dispatched to. - */ - public NullDispatcher() { - } - - /** - * Do nothing; all parameters are ignored. - */ - @Override - public Object dispatch(Method method, Object[] args) { - return null; - } -} diff --git a/core/java/android/hardware/camera2/dispatch/package.html b/core/java/android/hardware/camera2/dispatch/package.html deleted file mode 100644 index 783d0a1b54d5..000000000000 --- a/core/java/android/hardware/camera2/dispatch/package.html +++ /dev/null @@ -1,3 +0,0 @@ -<body> -{@hide} -</body> diff --git a/core/java/android/hardware/camera2/impl/CallbackProxies.java b/core/java/android/hardware/camera2/impl/CallbackProxies.java index c9eecf10e3d3..9e4cb80b89f0 100644 --- a/core/java/android/hardware/camera2/impl/CallbackProxies.java +++ b/core/java/android/hardware/camera2/impl/CallbackProxies.java @@ -15,16 +15,17 @@ */ package android.hardware.camera2.impl; +import android.os.Binder; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; -import android.hardware.camera2.dispatch.Dispatchable; -import android.hardware.camera2.dispatch.MethodNameInvoker; import android.view.Surface; +import java.util.concurrent.Executor; + import static com.android.internal.util.Preconditions.*; /** @@ -34,164 +35,86 @@ import static com.android.internal.util.Preconditions.*; * to use our own proxy mechanism.</p> */ public class CallbackProxies { - - // TODO: replace with codegen - - public static class DeviceStateCallbackProxy extends CameraDeviceImpl.StateCallbackKK { - private final MethodNameInvoker<CameraDeviceImpl.StateCallbackKK> mProxy; - - public DeviceStateCallbackProxy( - Dispatchable<CameraDeviceImpl.StateCallbackKK> dispatchTarget) { - dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null"); - mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.StateCallbackKK.class); - } - - @Override - public void onOpened(CameraDevice camera) { - mProxy.invoke("onOpened", camera); - } - - @Override - public void onDisconnected(CameraDevice camera) { - mProxy.invoke("onDisconnected", camera); - } - - @Override - public void onError(CameraDevice camera, int error) { - mProxy.invoke("onError", camera, error); - } - - @Override - public void onUnconfigured(CameraDevice camera) { - mProxy.invoke("onUnconfigured", camera); - } - - @Override - public void onActive(CameraDevice camera) { - mProxy.invoke("onActive", camera); - } - - @Override - public void onBusy(CameraDevice camera) { - mProxy.invoke("onBusy", camera); - } - - @Override - public void onClosed(CameraDevice camera) { - mProxy.invoke("onClosed", camera); - } - - @Override - public void onIdle(CameraDevice camera) { - mProxy.invoke("onIdle", camera); - } - } - - @SuppressWarnings("deprecation") - public static class DeviceCaptureCallbackProxy implements CameraDeviceImpl.CaptureCallback { - private final MethodNameInvoker<CameraDeviceImpl.CaptureCallback> mProxy; - - public DeviceCaptureCallbackProxy( - Dispatchable<CameraDeviceImpl.CaptureCallback> dispatchTarget) { - dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null"); - mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.CaptureCallback.class); - } - - @Override - public void onCaptureStarted(CameraDevice camera, - CaptureRequest request, long timestamp, long frameNumber) { - mProxy.invoke("onCaptureStarted", camera, request, timestamp, frameNumber); - } - - @Override - public void onCapturePartial(CameraDevice camera, - CaptureRequest request, CaptureResult result) { - mProxy.invoke("onCapturePartial", camera, request, result); - } - - @Override - public void onCaptureProgressed(CameraDevice camera, - CaptureRequest request, CaptureResult partialResult) { - mProxy.invoke("onCaptureProgressed", camera, request, partialResult); - } - - @Override - public void onCaptureCompleted(CameraDevice camera, - CaptureRequest request, TotalCaptureResult result) { - mProxy.invoke("onCaptureCompleted", camera, request, result); - } - - @Override - public void onCaptureFailed(CameraDevice camera, - CaptureRequest request, CaptureFailure failure) { - mProxy.invoke("onCaptureFailed", camera, request, failure); - } - - @Override - public void onCaptureSequenceCompleted(CameraDevice camera, - int sequenceId, long frameNumber) { - mProxy.invoke("onCaptureSequenceCompleted", camera, sequenceId, frameNumber); - } - - @Override - public void onCaptureSequenceAborted(CameraDevice camera, - int sequenceId) { - mProxy.invoke("onCaptureSequenceAborted", camera, sequenceId); - } - - @Override - public void onCaptureBufferLost(CameraDevice camera, - CaptureRequest request, Surface target, long frameNumber) { - mProxy.invoke("onCaptureBufferLost", camera, request, target, frameNumber); - } - - } - public static class SessionStateCallbackProxy extends CameraCaptureSession.StateCallback { - private final MethodNameInvoker<CameraCaptureSession.StateCallback> mProxy; + private final Executor mExecutor; + private final CameraCaptureSession.StateCallback mCallback; - public SessionStateCallbackProxy( - Dispatchable<CameraCaptureSession.StateCallback> dispatchTarget) { - dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null"); - mProxy = new MethodNameInvoker<>(dispatchTarget, - CameraCaptureSession.StateCallback.class); + public SessionStateCallbackProxy(Executor executor, + CameraCaptureSession.StateCallback callback) { + mExecutor = checkNotNull(executor, "executor must not be null"); + mCallback = checkNotNull(callback, "callback must not be null"); } @Override public void onConfigured(CameraCaptureSession session) { - mProxy.invoke("onConfigured", session); + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onConfigured(session)); + } finally { + Binder.restoreCallingIdentity(ident); + } } @Override public void onConfigureFailed(CameraCaptureSession session) { - mProxy.invoke("onConfigureFailed", session); + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onConfigureFailed(session)); + } finally { + Binder.restoreCallingIdentity(ident); + } } @Override public void onReady(CameraCaptureSession session) { - mProxy.invoke("onReady", session); + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onReady(session)); + } finally { + Binder.restoreCallingIdentity(ident); + } } @Override public void onActive(CameraCaptureSession session) { - mProxy.invoke("onActive", session); + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onActive(session)); + } finally { + Binder.restoreCallingIdentity(ident); + } } @Override public void onCaptureQueueEmpty(CameraCaptureSession session) { - mProxy.invoke("onCaptureQueueEmpty", session); + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onCaptureQueueEmpty(session)); + } finally { + Binder.restoreCallingIdentity(ident); + } } @Override public void onClosed(CameraCaptureSession session) { - mProxy.invoke("onClosed", session); + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onClosed(session)); + } finally { + Binder.restoreCallingIdentity(ident); + } } @Override public void onSurfacePrepared(CameraCaptureSession session, Surface surface) { - mProxy.invoke("onSurfacePrepared", session, surface); + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onSurfacePrepared(session, surface)); + } finally { + Binder.restoreCallingIdentity(ident); + } } } diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index 8b8bbc34f7d2..9cac71c35645 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -20,20 +20,17 @@ 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.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,16 +48,16 @@ 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; - /** Internal handler; used for all incoming events to preserve total order */ - private final Handler mDeviceHandler; + /** Internal executor; used for all incoming events to preserve total order */ + private final Executor mDeviceExecutor; /** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */ private final TaskDrainer<Integer> mSequenceDrainer; @@ -87,9 +84,9 @@ 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) { + Executor deviceStateExecutor, boolean configureSuccess) { if (callback == null) { throw new IllegalArgumentException("callback must not be null"); } @@ -98,10 +95,11 @@ 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"); + mDeviceExecutor = checkNotNull(deviceStateExecutor, + "deviceStateExecutor must not be null"); mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null"); /* @@ -110,11 +108,11 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession * This ensures total ordering between CameraDevice.StateCallback and * CameraDeviceImpl.CaptureCallback events. */ - mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(), + mSequenceDrainer = new TaskDrainer<>(mDeviceExecutor, new SequenceDrainListener(), /*name*/"seq"); - mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(), + mIdleDrainer = new TaskSingleDrainer(mDeviceExecutor, new IdleDrainListener(), /*name*/"idle"); - mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(), + mAbortDrainer = new TaskSingleDrainer(mDeviceExecutor, new AbortDrainListener(), /*name*/"abort"); // CameraDevice should call configureOutputs and have it finish before constructing us @@ -180,7 +178,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession } return addPendingSequence(mDeviceImpl.capture(request, - createCaptureCallbackProxy(handler, callback), mDeviceHandler)); + createCaptureCallbackProxy(handler, callback), mDeviceExecutor)); } } @@ -217,7 +215,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession } return addPendingSequence(mDeviceImpl.captureBurst(requests, - createCaptureCallbackProxy(handler, callback), mDeviceHandler)); + createCaptureCallbackProxy(handler, callback), mDeviceExecutor)); } } @@ -241,7 +239,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession } return addPendingSequence(mDeviceImpl.setRepeatingRequest(request, - createCaptureCallbackProxy(handler, callback), mDeviceHandler)); + createCaptureCallbackProxy(handler, callback), mDeviceExecutor)); } } @@ -274,7 +272,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession } return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests, - createCaptureCallbackProxy(handler, callback), mDeviceHandler)); + createCaptureCallbackProxy(handler, callback), mDeviceExecutor)); } } @@ -446,119 +444,145 @@ 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); } /** * - * Create an internal state callback, to be invoked on the mDeviceHandler + * Create an internal state callback, to be invoked on the mDeviceExecutor * * <p>It has a few behaviors: * <ul> diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java index 06c2c25ab6bb..89f6172df656 100644 --- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java @@ -33,6 +33,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.concurrent.Executor; import static com.android.internal.util.Preconditions.*; @@ -59,14 +60,14 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl * (e.g. no pending captures, no repeating requests, no flush).</p> */ CameraConstrainedHighSpeedCaptureSessionImpl(int id, - CameraCaptureSession.StateCallback callback, Handler stateHandler, + CameraCaptureSession.StateCallback callback, Executor stateExecutor, android.hardware.camera2.impl.CameraDeviceImpl deviceImpl, - Handler deviceStateHandler, boolean configureSuccess, + Executor deviceStateExecutor, 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, deviceStateExecutor, configureSuccess); } @Override diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 511fa43a5441..1f35f31b68ff 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -18,6 +18,7 @@ package android.hardware.camera2.impl; import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable; +import android.annotation.NonNull; import android.hardware.ICameraService; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; @@ -35,6 +36,7 @@ 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.IBinder; @@ -47,6 +49,8 @@ import android.util.Size; import android.util.SparseArray; import android.view.Surface; +import com.android.internal.util.Preconditions; + import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collection; @@ -58,6 +62,8 @@ 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 @@ -78,7 +84,7 @@ public class CameraDeviceImpl extends CameraDevice private final StateCallback mDeviceCallback; private volatile StateCallbackKK mSessionStateCallback; - private final Handler mDeviceHandler; + private final Executor mDeviceExecutor; private final AtomicBoolean mClosing = new AtomicBoolean(); private boolean mInError = false; @@ -241,7 +247,7 @@ public class CameraDeviceImpl extends CameraDevice } mCameraId = cameraId; mDeviceCallback = callback; - mDeviceHandler = handler; + mDeviceExecutor = checkAndWrapHandler(handler); mCharacteristics = characteristics; mAppTargetSdkVersion = appTargetSdkVersion; @@ -288,15 +294,15 @@ public class CameraDeviceImpl extends CameraDevice try { remoteDeviceBinder.linkToDeath(this, /*flag*/ 0); } catch (RemoteException e) { - CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected); + CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected); throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, "The camera device has encountered a serious error"); } } - mDeviceHandler.post(mCallOnOpened); - mDeviceHandler.post(mCallOnUnconfigured); + mDeviceExecutor.execute(mCallOnOpened); + mDeviceExecutor.execute(mCallOnUnconfigured); } } @@ -335,7 +341,7 @@ public class CameraDeviceImpl extends CameraDevice final boolean isError = failureIsError; synchronized(mInterfaceLock) { mInError = true; - mDeviceHandler.post(new Runnable() { + mDeviceExecutor.execute(new Runnable() { @Override public void run() { if (isError) { @@ -423,7 +429,7 @@ public class CameraDeviceImpl extends CameraDevice } } - mDeviceHandler.post(mCallOnBusy); + mDeviceExecutor.execute(mCallOnBusy); stopRepeating(); try { @@ -482,10 +488,10 @@ public class CameraDeviceImpl extends CameraDevice throw e; } finally { if (success && outputs.size() > 0) { - mDeviceHandler.post(mCallOnIdle); + mDeviceExecutor.execute(mCallOnIdle); } else { // Always return to the 'unconfigured' state if we didn't hit a fatal error - mDeviceHandler.post(mCallOnUnconfigured); + mDeviceExecutor.execute(mCallOnUnconfigured); } } } @@ -501,8 +507,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 +524,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 +544,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 +574,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 +590,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 +606,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 +621,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 +685,11 @@ public class CameraDeviceImpl extends CameraDevice SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config); newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++, - callback, handler, this, mDeviceHandler, configureSuccess, + callback, executor, this, mDeviceExecutor, configureSuccess, mCharacteristics); } else { newSession = new CameraCaptureSessionImpl(mNextSessionId++, input, - callback, handler, this, mDeviceHandler, - configureSuccess); + callback, executor, this, mDeviceExecutor, configureSuccess); } // TODO: wait until current session closes, then create the new session @@ -893,22 +904,22 @@ public class CameraDeviceImpl extends CameraDevice } } - public int capture(CaptureRequest request, CaptureCallback callback, Handler handler) + public int capture(CaptureRequest request, CaptureCallback callback, Executor executor) throws CameraAccessException { if (DEBUG) { Log.d(TAG, "calling capture"); } List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); requestList.add(request); - return submitCaptureRequest(requestList, callback, handler, /*streaming*/false); + return submitCaptureRequest(requestList, callback, executor, /*streaming*/false); } public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback, - Handler handler) throws CameraAccessException { + Executor executor) throws CameraAccessException { if (requests == null || requests.isEmpty()) { throw new IllegalArgumentException("At least one request must be given"); } - return submitCaptureRequest(requests, callback, handler, /*streaming*/false); + return submitCaptureRequest(requests, callback, executor, /*streaming*/false); } /** @@ -963,7 +974,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", @@ -982,11 +998,11 @@ public class CameraDeviceImpl extends CameraDevice } private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, - Handler handler, boolean repeating) throws CameraAccessException { + Executor executor, 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 = checkExecutor(executor, 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 +1056,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"); @@ -1059,7 +1075,7 @@ public class CameraDeviceImpl extends CameraDevice } if (mIdle) { - mDeviceHandler.post(mCallOnActive); + mDeviceExecutor.execute(mCallOnActive); } mIdle = false; @@ -1068,18 +1084,18 @@ public class CameraDeviceImpl extends CameraDevice } public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback, - Handler handler) throws CameraAccessException { + Executor executor) throws CameraAccessException { List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); requestList.add(request); - return submitCaptureRequest(requestList, callback, handler, /*streaming*/true); + return submitCaptureRequest(requestList, callback, executor, /*streaming*/true); } public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback, - Handler handler) throws CameraAccessException { + Executor executor) throws CameraAccessException { if (requests == null || requests.isEmpty()) { throw new IllegalArgumentException("At least one request must be given"); } - return submitCaptureRequest(requests, callback, handler, /*streaming*/true); + return submitCaptureRequest(requests, callback, executor, /*streaming*/true); } public void stopRepeating() throws CameraAccessException { @@ -1124,12 +1140,12 @@ public class CameraDeviceImpl extends CameraDevice synchronized(mInterfaceLock) { checkIfCameraClosedOrInError(); - mDeviceHandler.post(mCallOnBusy); + mDeviceExecutor.execute(mCallOnBusy); // If already idle, just do a busy->idle transition immediately, don't actually // flush. if (mIdle) { - mDeviceHandler.post(mCallOnIdle); + mDeviceExecutor.execute(mCallOnIdle); return; } @@ -1157,7 +1173,7 @@ public class CameraDeviceImpl extends CameraDevice // either a normal close where the remote device is valid // or a close after a startup error (no remote device but in error state) if (mRemoteDevice != null || mInError) { - mDeviceHandler.post(mCallOnClosed); + mDeviceExecutor.execute(mCallOnClosed); } mRemoteDevice = null; @@ -1354,7 +1370,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 +1382,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 +1441,8 @@ public class CameraDeviceImpl extends CameraDevice return getRequest(0); } - public Handler getHandler() { - return mHandler; + public Executor getExecutor() { + return mExecutor; } public int getSessionId() { @@ -1810,7 +1826,12 @@ public class CameraDeviceImpl extends CameraDevice } } }; - holder.getHandler().post(resultDispatch); + final long ident = Binder.clearCallingIdentity(); + try { + holder.getExecutor().execute(resultDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } } } } @@ -1838,7 +1859,12 @@ public class CameraDeviceImpl extends CameraDevice switch (errorCode) { case ERROR_CAMERA_DISCONNECTED: - CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected); + final long ident = Binder.clearCallingIdentity(); + try { + CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected); + } finally { + Binder.restoreCallingIdentity(ident); + } break; case ERROR_CAMERA_REQUEST: case ERROR_CAMERA_RESULT: @@ -1860,8 +1886,13 @@ public class CameraDeviceImpl extends CameraDevice private void scheduleNotifyError(int code) { mInError = true; - CameraDeviceImpl.this.mDeviceHandler.post(obtainRunnable( - CameraDeviceCallbacks::notifyError, this, code)); + final long ident = Binder.clearCallingIdentity(); + try { + CameraDeviceImpl.this.mDeviceExecutor.execute(obtainRunnable( + CameraDeviceCallbacks::notifyError, this, code)); + } finally { + Binder.restoreCallingIdentity(ident); + } } private void notifyError(int code) { @@ -1900,7 +1931,12 @@ public class CameraDeviceImpl extends CameraDevice if (mRemoteDevice == null) return; // Camera already closed if (!CameraDeviceImpl.this.mIdle) { - CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle); + final long ident = Binder.clearCallingIdentity(); + try { + CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnIdle); + } finally { + Binder.restoreCallingIdentity(ident); + } } CameraDeviceImpl.this.mIdle = true; } @@ -1929,36 +1965,41 @@ public class CameraDeviceImpl extends CameraDevice if (isClosed()) return; // Dispatch capture start notice - holder.getHandler().post( - new Runnable() { - @Override - public void run() { - if (!CameraDeviceImpl.this.isClosed()) { - final int subsequenceId = resultExtras.getSubsequenceId(); - final CaptureRequest request = holder.getRequest(subsequenceId); - - if (holder.hasBatchedOutputs()) { - // Send derived onCaptureStarted for requests within the batch - final Range<Integer> fpsRange = - request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); - for (int i = 0; i < holder.getRequestCount(); i++) { + final long ident = Binder.clearCallingIdentity(); + try { + holder.getExecutor().execute( + new Runnable() { + @Override + public void run() { + if (!CameraDeviceImpl.this.isClosed()) { + final int subsequenceId = resultExtras.getSubsequenceId(); + final CaptureRequest request = holder.getRequest(subsequenceId); + + if (holder.hasBatchedOutputs()) { + // Send derived onCaptureStarted for requests within the + // batch + final Range<Integer> fpsRange = + request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); + for (int i = 0; i < holder.getRequestCount(); i++) { + holder.getCallback().onCaptureStarted( + CameraDeviceImpl.this, + holder.getRequest(i), + timestamp - (subsequenceId - i) * + NANO_PER_SECOND/fpsRange.getUpper(), + frameNumber - (subsequenceId - i)); + } + } else { holder.getCallback().onCaptureStarted( CameraDeviceImpl.this, - holder.getRequest(i), - timestamp - (subsequenceId - i) * - NANO_PER_SECOND/fpsRange.getUpper(), - frameNumber - (subsequenceId - i)); + holder.getRequest(resultExtras.getSubsequenceId()), + timestamp, frameNumber); } - } else { - holder.getCallback().onCaptureStarted( - CameraDeviceImpl.this, - holder.getRequest(resultExtras.getSubsequenceId()), - timestamp, frameNumber); } } - } - }); - + }); + } finally { + Binder.restoreCallingIdentity(ident); + } } } @@ -2111,7 +2152,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 +2253,12 @@ public class CameraDeviceImpl extends CameraDevice } }; // Dispatch the failure callback - holder.getHandler().post(failureDispatch); + final long ident = Binder.clearCallingIdentity(); + try { + holder.getExecutor().execute(failureDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } } } else { boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT); @@ -2247,7 +2298,12 @@ public class CameraDeviceImpl extends CameraDevice checkAndFireSequenceComplete(); // Dispatch the failure callback - holder.getHandler().post(failureDispatch); + final long ident = Binder.clearCallingIdentity(); + try { + holder.getExecutor().execute(failureDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } } } @@ -2255,6 +2311,62 @@ public class CameraDeviceImpl extends CameraDevice } // public class CameraDeviceCallbacks /** + * A camera specific adapter {@link Executor} that posts all executed tasks onto the given + * {@link Handler}. + * + * @hide + */ + private static class CameraHandlerExecutor implements Executor { + private final Handler mHandler; + + public CameraHandlerExecutor(@NonNull Handler handler) { + mHandler = Preconditions.checkNotNull(handler); + } + + @Override + public void execute(Runnable command) { + // Return value of 'post()' will be ignored in order to keep the + // same camera behavior. For further details see b/74605221 . + mHandler.post(command); + } + } + + /** + * Default executor management. + * + * <p> + * If executor is null, get the current thread's + * Looper to create a Executor with. If no looper exists, throw + * {@code IllegalArgumentException}. + * </p> + */ + static Executor checkExecutor(Executor executor) { + return (executor == null) ? checkAndWrapHandler(null) : executor; + } + + /** + * Default executor management. + * + * <p>If the callback isn't null, check the executor, otherwise pass it through.</p> + */ + static <T> Executor checkExecutor(Executor executor, T callback) { + return (callback != null) ? checkExecutor(executor) : executor; + } + + /** + * 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> + */ + public static Executor checkAndWrapHandler(Handler handler) { + return new CameraHandlerExecutor(checkHandler(handler)); + } + + /** * Default handler management. * * <p> @@ -2328,6 +2440,11 @@ public class CameraDeviceImpl extends CameraDevice } } }; - CameraDeviceImpl.this.mDeviceHandler.post(r); + final long ident = Binder.clearCallingIdentity(); + try { + CameraDeviceImpl.this.mDeviceExecutor.execute(r); + } finally { + Binder.restoreCallingIdentity(ident); + } } } diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java index a79a6c17f925..7bdb4a2f1339 100644 --- a/core/java/android/hardware/camera2/params/SessionConfiguration.java +++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java @@ -17,10 +17,10 @@ package android.hardware.camera2.params; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.IntDef; -import android.os.Handler; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; @@ -31,6 +31,7 @@ import android.hardware.camera2.params.OutputConfiguration; import java.util.Collections; import java.util.List; import java.util.ArrayList; +import java.util.concurrent.Executor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -78,7 +79,7 @@ public final class SessionConfiguration { private List<OutputConfiguration> mOutputConfigurations; private CameraCaptureSession.StateCallback mStateCallback; private int mSessionType; - private Handler mHandler = null; + private Executor mExecutor = null; private InputConfiguration mInputConfig = null; private CaptureRequest mSessionParameters = null; @@ -87,10 +88,9 @@ public final class SessionConfiguration { * * @param sessionType The session type. * @param outputs A list of output configurations for the capture session. + * @param executor The executor which should be used to invoke the callback. In general it is + * recommended that camera operations are not done on the main (UI) thread. * @param cb A state callback interface implementation. - * @param handler The handler on which the callback will be invoked. If it is - * set to null, the callback will be invoked on the current thread's - * {@link android.os.Looper looper}. * * @see #SESSION_REGULAR * @see #SESSION_HIGH_SPEED @@ -101,11 +101,12 @@ public final class SessionConfiguration { */ public SessionConfiguration(@SessionMode int sessionType, @NonNull List<OutputConfiguration> outputs, - @NonNull CameraCaptureSession.StateCallback cb, @Nullable Handler handler) { + @NonNull @CallbackExecutor Executor executor, + @NonNull CameraCaptureSession.StateCallback cb) { mSessionType = sessionType; mOutputConfigurations = Collections.unmodifiableList(new ArrayList<>(outputs)); mStateCallback = cb; - mHandler = handler; + mExecutor = executor; } /** @@ -136,14 +137,12 @@ public final class SessionConfiguration { } /** - * Retrieve the {@link CameraCaptureSession.StateCallback} for the capture session. + * Retrieve the {@link java.util.concurrent.Executor} for the capture session. * - * @return The handler on which the callback will be invoked. If it is - * set to null, the callback will be invoked on the current thread's - * {@link android.os.Looper looper}. + * @return The Executor on which the callback will be invoked. */ - public Handler getHandler() { - return mHandler; + public Executor getExecutor() { + return mExecutor; } /** diff --git a/core/java/android/hardware/camera2/utils/TaskDrainer.java b/core/java/android/hardware/camera2/utils/TaskDrainer.java index ed30ff34afc8..e71f26a18792 100644 --- a/core/java/android/hardware/camera2/utils/TaskDrainer.java +++ b/core/java/android/hardware/camera2/utils/TaskDrainer.java @@ -15,11 +15,11 @@ */ package android.hardware.camera2.utils; -import android.os.Handler; import android.util.Log; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.Executor; import static com.android.internal.util.Preconditions.*; @@ -55,7 +55,7 @@ public class TaskDrainer<T> { private static final String TAG = "TaskDrainer"; private final boolean DEBUG = false; - private final Handler mHandler; + private final Executor mExecutor; private final DrainListener mListener; private final String mName; @@ -73,28 +73,27 @@ public class TaskDrainer<T> { /** * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener - * via the {@code handler}. + * via the {@code executor}. * - * @param handler a non-{@code null} handler to use to post runnables to + * @param executor a non-{@code null} executor to use for listener execution * @param listener a non-{@code null} listener where {@code onDrained} will be called */ - public TaskDrainer(Handler handler, DrainListener listener) { - mHandler = checkNotNull(handler, "handler must not be null"); + public TaskDrainer(Executor executor, DrainListener listener) { + mExecutor = checkNotNull(executor, "executor must not be null"); mListener = checkNotNull(listener, "listener must not be null"); mName = null; } /** * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener - * via the {@code handler}. + * via the {@code executor}. * - * @param handler a non-{@code null} handler to use to post runnables to + * @param executor a non-{@code null} executor to use for listener execution * @param listener a non-{@code null} listener where {@code onDrained} will be called * @param name an optional name used for debug logging */ - public TaskDrainer(Handler handler, DrainListener listener, String name) { - // XX: Probably don't need a handler at all here - mHandler = checkNotNull(handler, "handler must not be null"); + public TaskDrainer(Executor executor, DrainListener listener, String name) { + mExecutor = checkNotNull(executor, "executor must not be null"); mListener = checkNotNull(listener, "listener must not be null"); mName = name; } @@ -200,15 +199,12 @@ public class TaskDrainer<T> { } private void postDrained() { - mHandler.post(new Runnable() { - @Override - public void run() { + mExecutor.execute(() -> { if (DEBUG) { Log.v(TAG + "[" + mName + "]", "onDrained"); } mListener.onDrained(); - } }); } } diff --git a/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java b/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java index f6272c9e6a66..9615450be447 100644 --- a/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java +++ b/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java @@ -16,7 +16,8 @@ package android.hardware.camera2.utils; import android.hardware.camera2.utils.TaskDrainer.DrainListener; -import android.os.Handler; + +import java.util.concurrent.Executor; /** * Keep track of a single concurrent task starting and finishing; @@ -38,25 +39,25 @@ public class TaskSingleDrainer { /** * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener - * via the {@code handler}. + * via the {@code executor}. * - * @param handler a non-{@code null} handler to use to post runnables to + * @param executor a non-{@code null} executor to use for listener execution * @param listener a non-{@code null} listener where {@code onDrained} will be called */ - public TaskSingleDrainer(Handler handler, DrainListener listener) { - mTaskDrainer = new TaskDrainer<>(handler, listener); + public TaskSingleDrainer(Executor executor, DrainListener listener) { + mTaskDrainer = new TaskDrainer<>(executor, listener); } /** * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener - * via the {@code handler}. + * via the {@code executor}. * - * @param handler a non-{@code null} handler to use to post runnables to + * @param executor a non-{@code null} executor to use for listener execution * @param listener a non-{@code null} listener where {@code onDrained} will be called * @param name an optional name used for debug logging */ - public TaskSingleDrainer(Handler handler, DrainListener listener, String name) { - mTaskDrainer = new TaskDrainer<>(handler, listener, name); + public TaskSingleDrainer(Executor executor, DrainListener listener, String name) { + mTaskDrainer = new TaskDrainer<>(executor, listener, name); } /** |