1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.camera.camera2.internal;
18 
19 import android.graphics.PointF;
20 import android.graphics.Rect;
21 import android.hardware.camera2.CameraCharacteristics;
22 import android.hardware.camera2.CameraDevice;
23 import android.hardware.camera2.CaptureRequest;
24 import android.hardware.camera2.CaptureResult;
25 import android.hardware.camera2.params.MeteringRectangle;
26 import android.os.Build;
27 import android.util.Log;
28 import android.util.Rational;
29 
30 import androidx.annotation.OptIn;
31 import androidx.annotation.RequiresApi;
32 import androidx.annotation.VisibleForTesting;
33 import androidx.camera.camera2.impl.Camera2ImplConfig;
34 import androidx.camera.camera2.internal.annotation.CameraExecutor;
35 import androidx.camera.camera2.internal.compat.workaround.MeteringRegionCorrection;
36 import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
37 import androidx.camera.core.CameraControl;
38 import androidx.camera.core.FocusMeteringAction;
39 import androidx.camera.core.FocusMeteringResult;
40 import androidx.camera.core.ImageCapture;
41 import androidx.camera.core.Logger;
42 import androidx.camera.core.MeteringPoint;
43 import androidx.camera.core.impl.CameraCaptureCallback;
44 import androidx.camera.core.impl.CameraCaptureFailure;
45 import androidx.camera.core.impl.CameraCaptureResult;
46 import androidx.camera.core.impl.CameraControlInternal;
47 import androidx.camera.core.impl.CaptureConfig;
48 import androidx.camera.core.impl.Config;
49 import androidx.camera.core.impl.Quirks;
50 import androidx.camera.core.impl.annotation.ExecutedBy;
51 import androidx.camera.core.impl.utils.futures.Futures;
52 import androidx.concurrent.futures.CallbackToFutureAdapter;
53 import androidx.concurrent.futures.CallbackToFutureAdapter.Completer;
54 
55 import com.google.common.util.concurrent.ListenableFuture;
56 
57 import org.jspecify.annotations.NonNull;
58 import org.jspecify.annotations.Nullable;
59 
60 import java.util.ArrayList;
61 import java.util.Collections;
62 import java.util.List;
63 import java.util.concurrent.Executor;
64 import java.util.concurrent.ScheduledExecutorService;
65 import java.util.concurrent.ScheduledFuture;
66 import java.util.concurrent.TimeUnit;
67 
68 /**
69  * Implementation of focus and metering.
70  *
71  * <p>It is intended to be used within {@link Camera2CameraControlImpl} to implement the
72  * functionality of {@link Camera2CameraControlImpl#startFocusAndMetering(FocusMeteringAction)} and
73  * {@link Camera2CameraControlImpl#cancelFocusAndMetering()}. This class depends on
74  * {@link Camera2CameraControlImpl} to provide some low-level methods such as updateSessionConfig,
75  * triggerAfInternal and cancelAfAeTriggerInternal to achieve the focus and metering functions.
76  *
77  * <p>To wait for the auto-focus lock, it calls
78  * {@link Camera2CameraControlImpl
79  * #addCaptureResultListener(Camera2CameraControlImpl.CaptureResultListener)}
80  * to monitor the capture result. It also requires {@link ScheduledExecutorService} to schedule the
81  * auto-cancel event and {@link Executor} to ensure all the methods within this class are called
82  * in the same thread as the Camera2CameraControlImpl.
83  *
84  * <p>The {@link Camera2CameraControlImpl} calls
85  * {@link FocusMeteringControl#addFocusMeteringOptions} to construct the 3A regions and append
86  * them to all repeating requests and single requests.
87  */
88 @OptIn(markerClass = ExperimentalCamera2Interop.class)
89 class FocusMeteringControl {
90     private static final String TAG = "FocusMeteringControl";
91 
92     static final long AUTO_FOCUS_TIMEOUT_DURATION = 5000;
93     private final Camera2CameraControlImpl mCameraControl;
94     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
95     @CameraExecutor
96     final Executor mExecutor;
97     private final ScheduledExecutorService mScheduler;
98     private volatile boolean mIsActive = false;
99     private volatile Rational mPreviewAspectRatio = null;
100     private static final MeteringRectangle[] EMPTY_RECTANGLES = new MeteringRectangle[0];
101     private final @NonNull MeteringRegionCorrection mMeteringRegionCorrection;
102 
103     //******************** Should only be accessed by executor (WorkThread) ****************//
104     private boolean mIsInAfAutoMode = false;
105     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
106     @NonNull Integer mCurrentAfState = CaptureResult.CONTROL_AF_STATE_INACTIVE;
107     private ScheduledFuture<?> mAutoCancelHandle;
108     private ScheduledFuture<?> mAutoFocusTimeoutHandle;
109     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
110     long mFocusTimeoutCounter = 0;
111     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
112     boolean mIsAutoFocusCompleted = false;
113     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
114     boolean mIsFocusSuccessful = false;
115     private int mTemplate = CameraDevice.TEMPLATE_PREVIEW;
116 
117     private Camera2CameraControlImpl.CaptureResultListener mSessionListenerForFocus = null;
118     private Camera2CameraControlImpl.CaptureResultListener mSessionListenerForCancel = null;
119     private MeteringRectangle[] mAfRects = EMPTY_RECTANGLES;
120     private MeteringRectangle[] mAeRects = EMPTY_RECTANGLES;
121     private MeteringRectangle[] mAwbRects = EMPTY_RECTANGLES;
122     CallbackToFutureAdapter.Completer<FocusMeteringResult> mRunningActionCompleter = null;
123     CallbackToFutureAdapter.Completer<Void> mRunningCancelCompleter = null;
124 
125     private boolean mIsExternalFlashAeModeEnabled = false;
126     private Camera2CameraControlImpl.CaptureResultListener mSessionListenerForAeMode = null;
127     //**************************************************************************************//
128 
129 
130     /**
131      * Constructs a FocusMeteringControl.
132      *
133      * <p>All tasks scheduled by {@code scheduler} will be immediately executed by {@code executor}.
134      *
135      * @param cameraControl Camera control to which this FocusMeteringControl belongs.
136      * @param scheduler     Scheduler used for scheduling tasks in the future.
137      * @param executor      Camera executor used to run all tasks scheduled on {@code scheduler}.
138      */
FocusMeteringControl(@onNull Camera2CameraControlImpl cameraControl, @NonNull ScheduledExecutorService scheduler, @CameraExecutor @NonNull Executor executor, @NonNull Quirks cameraQuirks)139     FocusMeteringControl(@NonNull Camera2CameraControlImpl cameraControl,
140             @NonNull ScheduledExecutorService scheduler,
141             @CameraExecutor @NonNull Executor executor,
142             @NonNull Quirks cameraQuirks) {
143         mCameraControl = cameraControl;
144         mExecutor = executor;
145         mScheduler = scheduler;
146         mMeteringRegionCorrection = new MeteringRegionCorrection(cameraQuirks);
147     }
148 
149     /**
150      * Set current active state. Set active if it is ready to accept focus/metering operations.
151      *
152      * <p> In inactive state, startFocusAndMetering does nothing while cancelFocusAndMetering
153      * still works to cancel current operation. cancelFocusAndMetering is performed automatically
154      * when active state is changed to false.
155      */
156     @ExecutedBy("mExecutor")
setActive(boolean isActive)157     void setActive(boolean isActive) {
158         if (isActive == mIsActive) {
159             return;
160         }
161 
162         mIsActive = isActive;
163 
164         if (!mIsActive) {
165             cancelFocusAndMeteringWithoutAsyncResult();
166         }
167     }
168 
setPreviewAspectRatio(@ullable Rational previewAspectRatio)169     public void setPreviewAspectRatio(@Nullable Rational previewAspectRatio) {
170         mPreviewAspectRatio = previewAspectRatio;
171     }
172 
173     // Preview aspect ratio is used if SurfaceAspectRatio is not specified in MeteringPoint.
getDefaultAspectRatio()174     private Rational getDefaultAspectRatio() {
175         if (mPreviewAspectRatio != null) {
176             return mPreviewAspectRatio;
177         }
178 
179         Rect cropSensorRegion = mCameraControl.getCropSensorRegion();
180         return new Rational(cropSensorRegion.width(), cropSensorRegion.height());
181     }
182 
183     @ExecutedBy("mExecutor")
setTemplate(int template)184     void setTemplate(int template) {
185         mTemplate = template;
186     }
187 
188     /**
189      * Called by {@link Camera2CameraControlImpl} to append the 3A regions to the shared options. It
190      * applies to all repeating requests and single requests.
191      */
192     @ExecutedBy("mExecutor")
addFocusMeteringOptions(Camera2ImplConfig.@onNull Builder configBuilder)193     void addFocusMeteringOptions(Camera2ImplConfig.@NonNull Builder configBuilder) {
194 
195         int afMode = mIsInAfAutoMode
196                 ? CaptureRequest.CONTROL_AF_MODE_AUTO
197                 : getDefaultAfMode();
198 
199         configBuilder.setCaptureRequestOptionWithPriority(CaptureRequest.CONTROL_AF_MODE,
200                 mCameraControl.getSupportedAfMode(afMode), Config.OptionPriority.REQUIRED);
201 
202         if (mAfRects.length != 0) {
203             configBuilder.setCaptureRequestOptionWithPriority(CaptureRequest.CONTROL_AF_REGIONS,
204                     mAfRects, Config.OptionPriority.REQUIRED);
205         }
206         if (mAeRects.length != 0) {
207             configBuilder.setCaptureRequestOptionWithPriority(CaptureRequest.CONTROL_AE_REGIONS,
208                     mAeRects, Config.OptionPriority.REQUIRED);
209         }
210         if (mAwbRects.length != 0) {
211             configBuilder.setCaptureRequestOptionWithPriority(CaptureRequest.CONTROL_AWB_REGIONS,
212                     mAwbRects, Config.OptionPriority.REQUIRED);
213         }
214     }
215 
isValid(final @NonNull MeteringPoint pt)216     private static boolean isValid(final @NonNull MeteringPoint pt) {
217         return pt.getX() >= 0f && pt.getX() <= 1f && pt.getY() >= 0f && pt.getY() <= 1f;
218     }
219 
getFovAdjustedPoint(@onNull MeteringPoint meteringPoint, @NonNull Rational cropRegionAspectRatio, @NonNull Rational defaultAspectRatio, @FocusMeteringAction.MeteringMode int meteringMode, MeteringRegionCorrection correction)220     private static PointF getFovAdjustedPoint(@NonNull MeteringPoint meteringPoint,
221             @NonNull Rational cropRegionAspectRatio,
222             @NonNull Rational defaultAspectRatio,
223             @FocusMeteringAction.MeteringMode int meteringMode,
224             MeteringRegionCorrection correction) {
225         // Use default aspect ratio unless there is a custom aspect ratio in MeteringPoint.
226         Rational fovAspectRatio = defaultAspectRatio;
227         if (meteringPoint.getSurfaceAspectRatio() != null) {
228             fovAspectRatio = meteringPoint.getSurfaceAspectRatio();
229         }
230 
231         PointF adjustedPoint = correction.getCorrectedPoint(meteringPoint, meteringMode);
232         if (!fovAspectRatio.equals(cropRegionAspectRatio)) {
233             if (fovAspectRatio.compareTo(cropRegionAspectRatio) > 0) {
234                 // FOV is more narrow than crop region, top and down side of FOV is cropped.
235                 float heightOfCropRegion =
236                         (float) (fovAspectRatio.doubleValue()
237                                 / cropRegionAspectRatio.doubleValue());
238                 float top_padding = (float) ((heightOfCropRegion - 1.0) / 2);
239                 adjustedPoint.y = (top_padding + adjustedPoint.y) * (1 / heightOfCropRegion);
240 
241             } else {
242                 // FOV is wider than crop region, left and right side of FOV is cropped.
243                 float widthOfCropRegion =
244                         (float) (cropRegionAspectRatio.doubleValue()
245                                 / fovAspectRatio.doubleValue());
246                 float left_padding = (float) ((widthOfCropRegion - 1.0) / 2);
247                 adjustedPoint.x = (left_padding + adjustedPoint.x) * (1f / widthOfCropRegion);
248             }
249         }
250 
251         return adjustedPoint;
252     }
253 
getMeteringRect(MeteringPoint meteringPoint, PointF adjustedPoint, Rect cropRegion)254     private static MeteringRectangle getMeteringRect(MeteringPoint meteringPoint,
255             PointF adjustedPoint, Rect cropRegion) {
256         int centerX = (int) (cropRegion.left + adjustedPoint.x * cropRegion.width());
257         int centerY = (int) (cropRegion.top + adjustedPoint.y * cropRegion.height());
258 
259         int width = (int) (meteringPoint.getSize() * cropRegion.width());
260         int height = (int) (meteringPoint.getSize() * cropRegion.height());
261 
262         Rect focusRect = new Rect(centerX - width / 2, centerY - height / 2, centerX + width / 2,
263                 centerY + height / 2);
264 
265         focusRect.left = rangeLimit(focusRect.left, cropRegion.right, cropRegion.left);
266         focusRect.right = rangeLimit(focusRect.right, cropRegion.right, cropRegion.left);
267         focusRect.top = rangeLimit(focusRect.top, cropRegion.bottom, cropRegion.top);
268         focusRect.bottom = rangeLimit(focusRect.bottom, cropRegion.bottom, cropRegion.top);
269 
270         return new MeteringRectangle(focusRect, MeteringRectangle.METERING_WEIGHT_MAX);
271     }
272 
rangeLimit(int val, int max, int min)273     private static int rangeLimit(int val, int max, int min) {
274         return Math.min(Math.max(val, min), max);
275     }
276 
startFocusAndMetering( @onNull FocusMeteringAction action)277     @NonNull ListenableFuture<FocusMeteringResult> startFocusAndMetering(
278             @NonNull FocusMeteringAction action) {
279         return startFocusAndMetering(action, AUTO_FOCUS_TIMEOUT_DURATION);
280     }
281 
282     @VisibleForTesting
startFocusAndMetering( @onNull FocusMeteringAction action, long timeoutDurationMs)283     @NonNull ListenableFuture<FocusMeteringResult> startFocusAndMetering(
284             @NonNull FocusMeteringAction action, long timeoutDurationMs) {
285         return CallbackToFutureAdapter.getFuture(completer -> {
286             mExecutor.execute(
287                     () -> startFocusAndMeteringInternal(completer, action, timeoutDurationMs));
288             return "startFocusAndMetering";
289         });
290     }
291 
getMeteringRectangles( @onNull List<MeteringPoint> meteringPoints, int maxRegionCount, @NonNull Rational defaultAspectRatio, @NonNull Rect cropSensorRegion, @FocusMeteringAction.MeteringMode int meteringMode)292     private @NonNull List<MeteringRectangle> getMeteringRectangles(
293             @NonNull List<MeteringPoint> meteringPoints,
294             int maxRegionCount,
295             @NonNull Rational defaultAspectRatio,
296             @NonNull Rect cropSensorRegion,
297             @FocusMeteringAction.MeteringMode int meteringMode) {
298         if (meteringPoints.isEmpty() || maxRegionCount == 0) {
299             return Collections.emptyList();
300         }
301 
302         List<MeteringRectangle> meteringRectanglesList = new ArrayList<>();
303         Rational cropRegionAspectRatio = new Rational(cropSensorRegion.width(),
304                 cropSensorRegion.height());
305         for (MeteringPoint meteringPoint : meteringPoints) {
306             // Only enable at most maxRegionCount.
307             if (meteringRectanglesList.size() == maxRegionCount) {
308                 break;
309             }
310             if (!isValid(meteringPoint)) {
311                 continue;
312             }
313 
314             PointF adjustedPoint = getFovAdjustedPoint(meteringPoint, cropRegionAspectRatio,
315                     defaultAspectRatio, meteringMode, mMeteringRegionCorrection);
316             MeteringRectangle meteringRectangle = getMeteringRect(meteringPoint, adjustedPoint,
317                     cropSensorRegion);
318             if (meteringRectangle.getWidth() == 0 || meteringRectangle.getHeight() == 0) {
319                 continue;
320             }
321             meteringRectanglesList.add(meteringRectangle);
322         }
323 
324         return Collections.unmodifiableList(meteringRectanglesList);
325     }
326 
327     @ExecutedBy("mExecutor")
startFocusAndMeteringInternal(@onNull Completer<FocusMeteringResult> completer, @NonNull FocusMeteringAction action, long timeoutDurationMs)328     void startFocusAndMeteringInternal(@NonNull Completer<FocusMeteringResult> completer,
329             @NonNull FocusMeteringAction action, long timeoutDurationMs) {
330         if (!mIsActive) {
331             completer.setException(
332                     new CameraControl.OperationCanceledException("Camera is not active."));
333             return;
334         }
335 
336         Rect cropSensorRegion = mCameraControl.getCropSensorRegion();
337         Rational defaultAspectRatio = getDefaultAspectRatio();
338         List<MeteringRectangle> rectanglesAf =
339                 getMeteringRectangles(action.getMeteringPointsAf(),
340                         mCameraControl.getMaxAfRegionCount(),
341                         defaultAspectRatio, cropSensorRegion, FocusMeteringAction.FLAG_AF);
342         List<MeteringRectangle> rectanglesAe =
343                 getMeteringRectangles(action.getMeteringPointsAe(),
344                         mCameraControl.getMaxAeRegionCount(),
345                         defaultAspectRatio, cropSensorRegion, FocusMeteringAction.FLAG_AE);
346         List<MeteringRectangle> rectanglesAwb =
347                 getMeteringRectangles(action.getMeteringPointsAwb(),
348                         mCameraControl.getMaxAwbRegionCount(),
349                         defaultAspectRatio, cropSensorRegion, FocusMeteringAction.FLAG_AWB);
350 
351         if (rectanglesAf.isEmpty() && rectanglesAe.isEmpty() && rectanglesAwb.isEmpty()) {
352             completer.setException(
353                     new IllegalArgumentException("None of the specified AF/AE/AWB MeteringPoints "
354                             + "is supported on this camera."));
355             return;
356         }
357 
358         failActionFuture("Cancelled by another startFocusAndMetering()");
359         failCancelFuture("Cancelled by another startFocusAndMetering()");
360         disableAutoCancel();
361         mRunningActionCompleter = completer;
362 
363         executeMeteringAction(
364                 rectanglesAf.toArray(EMPTY_RECTANGLES),
365                 rectanglesAe.toArray(EMPTY_RECTANGLES),
366                 rectanglesAwb.toArray(EMPTY_RECTANGLES),
367                 action,
368                 timeoutDurationMs
369         );
370     }
371 
372     /**
373      * Trigger an AF scan.
374      *
375      * @param completer used to complete the associated {@link ListenableFuture} when the
376      *                  operation succeeds or fails. Passing null to simply ignore the result.
377      * @param overrideAeMode true for overriding AE_MODE to CONTROL_AE_MODE_ON
378      *
379      */
380     @ExecutedBy("mExecutor")
triggerAf(@ullable Completer<CameraCaptureResult> completer, boolean overrideAeMode)381     void triggerAf(@Nullable Completer<CameraCaptureResult> completer, boolean overrideAeMode) {
382         if (!mIsActive) {
383             if (completer != null) {
384                 completer.setException(
385                         new CameraControl.OperationCanceledException("Camera is not active."));
386             }
387             return;
388         }
389 
390         CaptureConfig.Builder builder = new CaptureConfig.Builder();
391         builder.setTemplateType(mTemplate);
392         builder.setUseRepeatingSurface(true);
393         Camera2ImplConfig.Builder configBuilder = new Camera2ImplConfig.Builder();
394         configBuilder.setCaptureRequestOption(CaptureRequest.CONTROL_AF_TRIGGER,
395                 CaptureRequest.CONTROL_AF_TRIGGER_START);
396 
397         if (overrideAeMode) {
398             // This option will override the AE_MODE option in repeating request.
399             // On many devices, triggering Af with CONTROL_AE_MODE_ON_ALWAYS_FLASH or
400             // CONTROL_AE_MODE_ON_AUTO_FLASH will fire the flash when it's low light.
401             // Override it to AE_MODE_ON to prevent from this issue.
402             configBuilder.setCaptureRequestOptionWithPriority(CaptureRequest.CONTROL_AE_MODE,
403                     mCameraControl.getSupportedAeMode(CaptureRequest.CONTROL_AE_MODE_ON),
404                     Config.OptionPriority.HIGH_PRIORITY_REQUIRED);
405         }
406         builder.addImplementationOptions(configBuilder.build());
407         builder.addCameraCaptureCallback(new CameraCaptureCallback() {
408             @Override
409             public void onCaptureCompleted(int captureConfigId,
410                     @NonNull CameraCaptureResult cameraCaptureResult) {
411                 if (completer != null) {
412                     completer.set(cameraCaptureResult);
413                 }
414             }
415 
416             @Override
417             public void onCaptureFailed(int captureConfigId,
418                     @NonNull CameraCaptureFailure failure) {
419                 if (completer != null) {
420                     completer.setException(
421                             new CameraControlInternal.CameraControlException(failure));
422                 }
423             }
424 
425             @Override
426             public void onCaptureCancelled(int captureConfigId) {
427                 if (completer != null) {
428                     completer.setException(
429                             new CameraControl.OperationCanceledException("Camera is closed"));
430                 }
431             }
432         });
433 
434         mCameraControl.submitCaptureRequestsInternal(Collections.singletonList(builder.build()));
435     }
436 
437     /**
438      * Returns a {@link ListenableFuture} as result after triggering AE precapture.
439      */
triggerAePrecapture()440     ListenableFuture<Void> triggerAePrecapture() {
441         return CallbackToFutureAdapter.getFuture(completer -> {
442             mExecutor.execute(() -> {
443                 triggerAePrecapture(completer);
444             });
445             return "triggerAePrecapture";
446         });
447     }
448 
449     /**
450      * Trigger an AE precapture sequence.
451      *
452      * @param completer used to complete the associated {@link ListenableFuture} when the
453      *                  operation succeeds or fails. Passing null to simply ignore the result.
454      */
455     @ExecutedBy("mExecutor")
456     void triggerAePrecapture(@Nullable Completer<Void> completer) {
457         Logger.d(TAG, "triggerAePrecapture");
458 
459         if (!mIsActive) {
460             if (completer != null) {
461                 completer.setException(
462                         new CameraControl.OperationCanceledException("Camera is not active."));
463             }
464             return;
465         }
466 
467         CaptureConfig.Builder builder = new CaptureConfig.Builder();
468         builder.setTemplateType(mTemplate);
469         builder.setUseRepeatingSurface(true);
470         Camera2ImplConfig.Builder configBuilder = new Camera2ImplConfig.Builder();
471         configBuilder.setCaptureRequestOption(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
472                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
473         builder.addImplementationOptions(configBuilder.build());
474         builder.addCameraCaptureCallback(new CameraCaptureCallback() {
475             @Override
476             public void onCaptureCompleted(int captureConfigId,
477                     @NonNull CameraCaptureResult cameraCaptureResult) {
478                 if (completer != null) {
479                     Logger.d(TAG, "triggerAePrecapture: triggering capture request completed");
480                     completer.set(null);
481                 }
482             }
483 
484             @Override
485             public void onCaptureFailed(int captureConfigId,
486                     @NonNull CameraCaptureFailure failure) {
487                 if (completer != null) {
488                     completer.setException(
489                             new CameraControlInternal.CameraControlException(failure));
490                 }
491             }
492 
493             @Override
494             public void onCaptureCancelled(int captureConfigId) {
495                 if (completer != null) {
496                     completer.setException(
497                             new CameraControl.OperationCanceledException("Camera is closed"));
498                 }
499             }
500         });
501         mCameraControl.submitCaptureRequestsInternal(Collections.singletonList(builder.build()));
502     }
503 
504     @ExecutedBy("mExecutor")
505     void cancelAfAeTrigger(final boolean cancelAfTrigger,
506             final boolean cancelAePrecaptureTrigger) {
507         if (!mIsActive) {
508             return;
509         }
510 
511         CaptureConfig.Builder builder = new CaptureConfig.Builder();
512         builder.setUseRepeatingSurface(true);
513         builder.setTemplateType(mTemplate);
514 
515         Camera2ImplConfig.Builder configBuilder = new Camera2ImplConfig.Builder();
516         if (cancelAfTrigger) {
517             configBuilder.setCaptureRequestOption(CaptureRequest.CONTROL_AF_TRIGGER,
518                     CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
519         }
520         if (Build.VERSION.SDK_INT >= 23 && cancelAePrecaptureTrigger) {
521             configBuilder.setCaptureRequestOption(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
522                     CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL);
523         }
524         builder.addImplementationOptions(configBuilder.build());
525         mCameraControl.submitCaptureRequestsInternal(Collections.singletonList(builder.build()));
526     }
527 
528     /**
529      * Returns whether external flash AE mode is enabled.
530      *
531      * @see #enableExternalFlashAeMode
532      */
533     boolean isExternalFlashAeModeEnabled() {
534         return mIsExternalFlashAeModeEnabled;
535     }
536 
537     /**
538      * Enables or disables AE_MODE_ON_EXTERNAL_FLASH.
539      *
540      * <p> It will be enabled only if the AE mode is supported i.e. API >= 28 and available in
541      * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES}, and the flash mode is actually
542      * external (i.e. not the usual physical flash unit attached near camera) which is only
543      * {@link ImageCapture#FLASH_MODE_SCREEN} as of now. In case of other flash modes, the AE mode
544      * may get overwritten in {@link Camera2CameraControlImpl#getSessionOptions} and the future
545      * will never complete.
546      *
547      * @param enable Whether to enable or disable the AE mode.
548      * @return A {@link ListenableFuture} that is completed when the capture request to set the
549      *         AE mode has been processed in framework side.
550      */
551     ListenableFuture<Void> enableExternalFlashAeMode(boolean enable) {
552         if (Build.VERSION.SDK_INT < 28) {
553             Log.d(TAG, "CONTROL_AE_MODE_ON_EXTERNAL_FLASH is not supported in API "
554                     + Build.VERSION.SDK_INT);
555             return Futures.immediateFuture(null);
556         }
557 
558         if (mCameraControl.getSupportedAeMode(CaptureRequest.CONTROL_AE_MODE_ON_EXTERNAL_FLASH)
559                 != CaptureRequest.CONTROL_AE_MODE_ON_EXTERNAL_FLASH) {
560             Log.d(TAG, "CONTROL_AE_MODE_ON_EXTERNAL_FLASH is not supported in this device");
561             return Futures.immediateFuture(null);
562         }
563 
564         Log.d(TAG, "enableExternalFlashAeMode: CONTROL_AE_MODE_ON_EXTERNAL_FLASH supported");
565 
566         return CallbackToFutureAdapter.getFuture(completer -> {
567             mExecutor.execute(() -> {
568                 mCameraControl.removeCaptureResultListener(mSessionListenerForAeMode);
569                 mIsExternalFlashAeModeEnabled = enable;
570                 enableExternalFlashAeMode(completer);
571             });
572             return "enableExternalFlashAeMode";
573         });
574     }
575 
576     /**
577      * Enables or disables AE_MODE_ON_EXTERNAL_FLASH.
578      *
579      * @param completer used to complete the associated {@link ListenableFuture} when the
580      *                  operation succeeds or fails. Passing null to simply ignore the result.
581      *
582      * @see #enableExternalFlashAeMode
583      */
584     @RequiresApi(28)
585     @ExecutedBy("mExecutor")
586     private void enableExternalFlashAeMode(@Nullable Completer<Void> completer) {
587         if (!mIsActive) {
588             if (completer != null) {
589                 completer.setException(
590                         new CameraControl.OperationCanceledException("Camera is not active."));
591             }
592             return;
593         }
594 
595         long sessionUpdateId = mCameraControl.updateSessionConfigSynchronous();
596 
597         // Will be called on mExecutor since mSessionCallback was created with mExecutor
598         mSessionListenerForAeMode =
599                 result -> {
600                     boolean isAeModeExternalFlash = result.get(CaptureResult.CONTROL_AE_MODE)
601                             == CaptureRequest.CONTROL_AE_MODE_ON_EXTERNAL_FLASH;
602                     Logger.d(TAG, "enableExternalFlashAeMode: "
603                             + "isAeModeExternalFlash = " + isAeModeExternalFlash);
604 
605                     // Check if the AE mode is as desired
606                     // TODO: Currently this check will never pass if AE mode request is overwritten
607                     //  due to other flash mode in Camera2CameraControlImpl#getSessionOptions. To
608                     //  handle this gracefully, we should have a central code controlling the AE
609                     //  mode value to set to capture requests and we can compare with that instead.
610                     if (isAeModeExternalFlash == mIsExternalFlashAeModeEnabled) {
611                         // Ensure the session is actually updated
612                         if (Camera2CameraControlImpl.isSessionUpdated(result, sessionUpdateId)) {
613                             Logger.d(TAG, "enableExternalFlashAeMode: session updated with "
614                                     + "isAeModeExternalFlash = " + isAeModeExternalFlash);
615                             if (completer != null) {
616                                 completer.set(null);
617                             }
618                             return true; // remove this listener
619                         }
620                     }
621 
622                     return false; // continue checking
623                 };
624 
625         mCameraControl.addCaptureResultListener(mSessionListenerForAeMode);
626     }
627 
628     @ExecutedBy("mExecutor")
629     private void disableAutoCancel() {
630         if (mAutoCancelHandle != null) {
631             mAutoCancelHandle.cancel(/*mayInterruptIfRunning=*/true);
632             mAutoCancelHandle = null;
633         }
634     }
635 
636     @ExecutedBy("mExecutor")
637     private void clearAutoFocusTimeoutHandle() {
638         if (mAutoFocusTimeoutHandle != null) {
639             mAutoFocusTimeoutHandle.cancel(/*mayInterruptIfRunning=*/true);
640             mAutoFocusTimeoutHandle = null;
641         }
642     }
643 
644     @VisibleForTesting
645     @ExecutedBy("mExecutor")
646     int getDefaultAfMode() {
647         switch (mTemplate) {
648             case CameraDevice.TEMPLATE_RECORD:
649                 return CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO;
650             case CameraDevice.TEMPLATE_PREVIEW:
651             default:
652                 return CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
653         }
654     }
655 
656     private boolean isAfModeSupported() {
657         return mCameraControl.getSupportedAfMode(CaptureRequest.CONTROL_AF_MODE_AUTO)
658                 == CaptureRequest.CONTROL_AF_MODE_AUTO;
659     }
660 
661     @ExecutedBy("mExecutor")
662     void completeActionFuture(boolean isFocusSuccessful) {
663         clearAutoFocusTimeoutHandle();
664         if (mRunningActionCompleter != null) {
665             mRunningActionCompleter.set(FocusMeteringResult.create(isFocusSuccessful));
666             mRunningActionCompleter = null;
667         }
668     }
669 
670     @ExecutedBy("mExecutor")
671     private void failActionFuture(String message) {
672         mCameraControl.removeCaptureResultListener(mSessionListenerForFocus);
673         if (mRunningActionCompleter != null) {
674             mRunningActionCompleter.setException(new CameraControl.OperationCanceledException(
675                     message));
676             mRunningActionCompleter = null;
677         }
678     }
679 
680     @ExecutedBy("mExecutor")
681     private void failCancelFuture(String message) {
682         mCameraControl.removeCaptureResultListener(mSessionListenerForCancel);
683         if (mRunningCancelCompleter != null) {
684             mRunningCancelCompleter.setException(
685                     new CameraControl.OperationCanceledException(message));
686             mRunningCancelCompleter = null;
687         }
688     }
689 
690     @ExecutedBy("mExecutor")
691     private void completeCancelFuture() {
692         if (mRunningCancelCompleter != null) {
693             mRunningCancelCompleter.set(null);
694             mRunningCancelCompleter = null;
695         }
696     }
697 
698     @ExecutedBy("mExecutor")
699     private void executeMeteringAction(
700             MeteringRectangle @NonNull [] afRects,
701             MeteringRectangle @NonNull [] aeRects,
702             MeteringRectangle @NonNull [] awbRects,
703             FocusMeteringAction focusMeteringAction,
704             long timeoutDurationMs) {
705         mCameraControl.removeCaptureResultListener(mSessionListenerForFocus);
706 
707         disableAutoCancel();
708         clearAutoFocusTimeoutHandle();
709 
710         mAfRects = afRects;
711         mAeRects = aeRects;
712         mAwbRects = awbRects;
713 
714         long sessionUpdateId;
715         // Trigger AF scan if any AF points are added.
716         if (shouldTriggerAF()) {
717             mIsInAfAutoMode = true;
718             mIsAutoFocusCompleted = false;
719             mIsFocusSuccessful = false;
720             sessionUpdateId = mCameraControl.updateSessionConfigSynchronous();
721             triggerAf(null, /* overrideAeMode */ true);
722         } else {
723             mIsInAfAutoMode = false;
724             mIsAutoFocusCompleted = true; // Don't need to wait for auto-focus
725             mIsFocusSuccessful = false;  // False because AF is not triggered.
726             sessionUpdateId = mCameraControl.updateSessionConfigSynchronous();
727         }
728 
729         mCurrentAfState = CaptureResult.CONTROL_AF_STATE_INACTIVE;
730         final boolean isAfModeSupported = isAfModeSupported();
731 
732         // Will be called on mExecutor since mSessionCallback was created with mExecutor
733         mSessionListenerForFocus =
734                 result -> {
735                     Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
736                     if (shouldTriggerAF()) {
737                         if (!isAfModeSupported || afState == null) {
738                             // set isFocusSuccessful to true when camera does not support AF_AUTO.
739                             mIsFocusSuccessful = true;
740                             mIsAutoFocusCompleted = true;
741                         } else if (mCurrentAfState == CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN) {
742                             if (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED) {
743                                 mIsFocusSuccessful = true;
744                                 mIsAutoFocusCompleted = true;
745                             } else if (afState
746                                     == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
747                                 mIsFocusSuccessful = false;
748                                 mIsAutoFocusCompleted = true;
749                             }
750                         }
751                     }
752 
753                     // Check 3A regions
754                     if (mIsAutoFocusCompleted) {
755                         if (Camera2CameraControlImpl.isSessionUpdated(result, sessionUpdateId)) {
756                             completeActionFuture(mIsFocusSuccessful);
757                             return true; // remove this listener
758                         }
759                     }
760 
761                     if (!mCurrentAfState.equals(afState) && afState != null) {
762                         mCurrentAfState = afState;
763                     }
764                     return false; // continue checking
765                 };
766 
767         mCameraControl.addCaptureResultListener(mSessionListenerForFocus);
768 
769         final long timeoutId = ++mFocusTimeoutCounter;
770 
771         // Sets auto focus timeout runnable first so that action will be completed with
772         // mIsFocusSuccessful is false when auto cancel is enabled with the same default 5000ms
773         // duration.
774         final Runnable autoFocusTimeoutRunnable = () -> mExecutor.execute(() -> {
775             if (timeoutId == mFocusTimeoutCounter) {
776                 mIsFocusSuccessful = false;
777                 completeActionFuture(mIsFocusSuccessful);
778             }
779         });
780 
781         mAutoFocusTimeoutHandle = mScheduler.schedule(autoFocusTimeoutRunnable,
782                 timeoutDurationMs,
783                 TimeUnit.MILLISECONDS);
784 
785         if (focusMeteringAction.isAutoCancelEnabled()) {
786             final Runnable autoCancelRunnable = () -> mExecutor.execute(() -> {
787                 if (timeoutId == mFocusTimeoutCounter) {
788                     cancelFocusAndMeteringWithoutAsyncResult();
789                 }
790             });
791 
792             mAutoCancelHandle = mScheduler.schedule(autoCancelRunnable,
793                     focusMeteringAction.getAutoCancelDurationInMillis(),
794                     TimeUnit.MILLISECONDS);
795         }
796     }
797 
798     @ExecutedBy("mExecutor")
799     private boolean shouldTriggerAF() {
800         return mAfRects.length > 0;
801     }
802 
803     ListenableFuture<Void> cancelFocusAndMetering() {
804         return CallbackToFutureAdapter.getFuture(
805                 completer -> {
806                     mExecutor.execute(() -> cancelFocusAndMeteringInternal(completer));
807                     return "cancelFocusAndMetering";
808                 });
809     }
810 
811     @ExecutedBy("mExecutor")
812     void cancelFocusAndMeteringWithoutAsyncResult() {
813         cancelFocusAndMeteringInternal(null);
814     }
815 
816     @ExecutedBy("mExecutor")
817     void cancelFocusAndMeteringInternal(
818             CallbackToFutureAdapter.@Nullable Completer<Void> completer) {
819         failCancelFuture("Cancelled by another cancelFocusAndMetering()");
820         failActionFuture("Cancelled by cancelFocusAndMetering()");
821         mRunningCancelCompleter = completer;
822         disableAutoCancel();
823         clearAutoFocusTimeoutHandle();
824 
825         if (shouldTriggerAF()) {
826             cancelAfAeTrigger(true, false);
827         }
828         mAfRects = EMPTY_RECTANGLES;
829         mAeRects = EMPTY_RECTANGLES;
830         mAwbRects = EMPTY_RECTANGLES;
831 
832         mIsInAfAutoMode = false;
833         long sessionUpdateId = mCameraControl.updateSessionConfigSynchronous();
834 
835         if (mRunningCancelCompleter != null) {
836             int targetAfMode = mCameraControl.getSupportedAfMode(getDefaultAfMode());
837             mSessionListenerForCancel =
838                     captureResult -> {
839                         Integer afMode = captureResult.get(CaptureResult.CONTROL_AF_MODE);
840                         if (afMode == targetAfMode
841                                 && Camera2CameraControlImpl.isSessionUpdated(captureResult,
842                                 sessionUpdateId)) {
843                             completeCancelFuture();
844                             return true; // remove this listener
845                         }
846                         return false;
847                     };
848 
849             mCameraControl.addCaptureResultListener(mSessionListenerForCancel);
850         }
851     }
852 
853     boolean isFocusMeteringSupported(@NonNull FocusMeteringAction action) {
854         Rect cropSensorRegion = mCameraControl.getCropSensorRegion();
855         Rational defaultAspectRatio = getDefaultAspectRatio();
856         List<MeteringRectangle> rectanglesAf =
857                 getMeteringRectangles(action.getMeteringPointsAf(),
858                         mCameraControl.getMaxAfRegionCount(),
859                         defaultAspectRatio, cropSensorRegion, FocusMeteringAction.FLAG_AF);
860         List<MeteringRectangle> rectanglesAe =
861                 getMeteringRectangles(action.getMeteringPointsAe(),
862                         mCameraControl.getMaxAeRegionCount(),
863                         defaultAspectRatio, cropSensorRegion, FocusMeteringAction.FLAG_AE);
864         List<MeteringRectangle> rectanglesAwb =
865                 getMeteringRectangles(action.getMeteringPointsAwb(),
866                         mCameraControl.getMaxAwbRegionCount(),
867                         defaultAspectRatio, cropSensorRegion, FocusMeteringAction.FLAG_AWB);
868         return !rectanglesAf.isEmpty() || !rectanglesAe.isEmpty() || !rectanglesAwb.isEmpty();
869     }
870 }
871