SynchronizedCaptureSessionImpl.java
/*
* Copyright 2020 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 androidx.camera.camera2.internal;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.os.Handler;
import android.view.Surface;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.camera.camera2.internal.annotation.CameraExecutor;
import androidx.camera.camera2.internal.compat.params.SessionConfigurationCompat;
import androidx.camera.camera2.internal.compat.workaround.ForceCloseCaptureSession;
import androidx.camera.camera2.internal.compat.workaround.ForceCloseDeferrableSurface;
import androidx.camera.camera2.internal.compat.workaround.RequestMonitor;
import androidx.camera.camera2.internal.compat.workaround.SessionResetPolicy;
import androidx.camera.core.Logger;
import androidx.camera.core.impl.DeferrableSurface;
import androidx.camera.core.impl.Quirks;
import androidx.camera.core.impl.annotation.ExecutedBy;
import androidx.camera.core.impl.utils.futures.FutureChain;
import androidx.camera.core.impl.utils.futures.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* The SynchronizedCaptureSessionImpl applies workarounds for Quirks.
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
class SynchronizedCaptureSessionImpl extends SynchronizedCaptureSessionBaseImpl {
private static final String TAG = "SyncCaptureSessionImpl";
@NonNull
private final ScheduledExecutorService mScheduledExecutorService;
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
private final Object mObjectLock = new Object();
@Nullable
@GuardedBy("mObjectLock")
private List<DeferrableSurface> mDeferrableSurfaces;
@Nullable
@GuardedBy("mObjectLock")
ListenableFuture<List<Void>> mOpenSessionBlockerFuture;
private final ForceCloseDeferrableSurface mCloseSurfaceQuirk;
private final ForceCloseCaptureSession mForceCloseSessionQuirk;
private final RequestMonitor mRequestMonitor;
private final SessionResetPolicy mSessionResetPolicy;
private final AtomicBoolean mClosed = new AtomicBoolean(false);
SynchronizedCaptureSessionImpl(
@NonNull Quirks cameraQuirks,
@NonNull Quirks deviceQuirks,
@NonNull CaptureSessionRepository repository,
@NonNull @CameraExecutor Executor executor,
@NonNull ScheduledExecutorService scheduledExecutorService,
@NonNull Handler compatHandler) {
super(repository, executor, scheduledExecutorService, compatHandler);
mCloseSurfaceQuirk = new ForceCloseDeferrableSurface(cameraQuirks, deviceQuirks);
mRequestMonitor = new RequestMonitor(cameraQuirks);
mForceCloseSessionQuirk = new ForceCloseCaptureSession(deviceQuirks);
mSessionResetPolicy = new SessionResetPolicy(deviceQuirks);
mScheduledExecutorService = scheduledExecutorService;
}
@ExecutedBy("mExecutor")
@NonNull
@Override
public ListenableFuture<Void> openCaptureSession(@NonNull CameraDevice cameraDevice,
@NonNull SessionConfigurationCompat sessionConfigurationCompat,
@NonNull List<DeferrableSurface> deferrableSurfaces) {
synchronized (mObjectLock) {
// For b/146773463: It needs to check all the configured capture sessions are ready for
// opening next capture session.
List<SynchronizedCaptureSession>
configured = mCaptureSessionRepository.getCaptureSessions();
List<ListenableFuture<Void>> futureList = new ArrayList<>();
for (SynchronizedCaptureSession session : configured) {
futureList.add(session.getOpeningBlocker());
}
mOpenSessionBlockerFuture = Futures.successfulAsList(futureList);
return Futures.nonCancellationPropagating(
FutureChain.from(mOpenSessionBlockerFuture).transformAsync(v -> {
if (mSessionResetPolicy.needAbortCapture()) {
closeCreatedSession();
}
debugLog("start openCaptureSession");
return super.openCaptureSession(cameraDevice, sessionConfigurationCompat,
deferrableSurfaces);
}, getExecutor()));
}
}
@ExecutedBy("mExecutor")
private void closeCreatedSession() {
List<SynchronizedCaptureSession> sessions = mCaptureSessionRepository.getCaptureSessions();
for (SynchronizedCaptureSession session : sessions) {
session.close();
}
}
@ExecutedBy("mExecutor")
@NonNull
@Override
public ListenableFuture<Void> getOpeningBlocker() {
return Futures.makeTimeoutFuture(1500, mScheduledExecutorService,
mRequestMonitor.getRequestsProcessedFuture());
}
@ExecutedBy("mExecutor")
@NonNull
@Override
public ListenableFuture<List<Surface>> startWithDeferrableSurface(
@NonNull List<DeferrableSurface> deferrableSurfaces, long timeout) {
synchronized (mObjectLock) {
mDeferrableSurfaces = deferrableSurfaces;
return super.startWithDeferrableSurface(deferrableSurfaces, timeout);
}
}
@ExecutedBy("mExecutor")
@Override
public boolean stop() {
synchronized (mObjectLock) {
if (isCameraCaptureSessionOpen()) {
mCloseSurfaceQuirk.onSessionEnd(mDeferrableSurfaces);
} else {
if (mOpenSessionBlockerFuture != null) {
mOpenSessionBlockerFuture.cancel(true);
}
}
return super.stop();
}
}
@ExecutedBy("mExecutor")
@Override
public int setSingleRepeatingRequest(@NonNull CaptureRequest request,
@NonNull CameraCaptureSession.CaptureCallback listener) throws CameraAccessException {
return mRequestMonitor.setSingleRepeatingRequest(
request, listener, super::setSingleRepeatingRequest);
}
@ExecutedBy("mExecutor")
@Override
public int captureBurstRequests(@NonNull List<CaptureRequest> requests,
@NonNull CameraCaptureSession.CaptureCallback listener) throws CameraAccessException {
return mRequestMonitor.captureBurstRequests(
requests, listener, super::captureBurstRequests);
}
@Override
public void onConfigured(@NonNull SynchronizedCaptureSession session) {
debugLog("Session onConfigured()");
mForceCloseSessionQuirk.onSessionConfigured(session,
mCaptureSessionRepository.getCreatingCaptureSessions(),
mCaptureSessionRepository.getCaptureSessions(),
super::onConfigured);
}
@ExecutedBy("mExecutor")
@Override
public void close() {
if (!mClosed.compareAndSet(false, true)) {
debugLog("close() has been called. Skip this invocation.");
return;
}
if (mSessionResetPolicy.needAbortCapture()) {
try {
debugLog("Call abortCaptures() before closing session.");
abortCaptures();
} catch (Exception e) {
debugLog("Exception when calling abortCaptures()" + e);
}
}
debugLog("Session call close()");
mRequestMonitor.getRequestsProcessedFuture().addListener(() -> {
// Checks the capture session is ready before closing. See: b/146773463.
debugLog("Session call super.close()");
super.close();
}, getExecutor());
}
@Override
public void onClosed(@NonNull SynchronizedCaptureSession session) {
synchronized (mObjectLock) {
mCloseSurfaceQuirk.onSessionEnd(mDeferrableSurfaces);
}
debugLog("onClosed()");
super.onClosed(session);
}
@Override
public void finishClose() {
super.finishClose();
mRequestMonitor.stop();
}
@ExecutedBy("mExecutor")
@Override
public void onCameraDeviceError(int error) {
super.onCameraDeviceError(error);
if (error == CameraDevice.StateCallback.ERROR_CAMERA_SERVICE) {
synchronized (mObjectLock) {
if (isCameraCaptureSessionOpen() && mDeferrableSurfaces != null) {
debugLog("Close DeferrableSurfaces for CameraDevice error.");
// b/290861504#comment4, close the DeferrableSurfaces.
for (DeferrableSurface deferrableSurface : mDeferrableSurfaces) {
deferrableSurface.close();
}
}
}
}
}
void debugLog(String message) {
Logger.d(TAG, "[" + SynchronizedCaptureSessionImpl.this + "] " + message);
}
}