1 /* 2 * Copyright 2020 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.hardware.camera2.CameraCaptureSession; 20 import android.hardware.camera2.CameraDevice; 21 22 import androidx.annotation.GuardedBy; 23 import androidx.camera.camera2.internal.annotation.CameraExecutor; 24 25 import org.jspecify.annotations.NonNull; 26 27 import java.util.ArrayList; 28 import java.util.LinkedHashSet; 29 import java.util.List; 30 import java.util.Set; 31 import java.util.concurrent.Executor; 32 33 /** 34 * The repository to maintain a list of the created and releasing SynchronizedCaptureSession. 35 * 36 * <p> The repository also help to close the created SynchronizedCaptureSession when the camera is 37 * disconnected. 38 */ 39 class CaptureSessionRepository { 40 /** Executor for all the callbacks from the {@link CameraCaptureSession}. */ 41 @CameraExecutor 42 final @NonNull Executor mExecutor; 43 44 @SuppressWarnings("WeakerAccess") /* synthetic accessor */ 45 final Object mLock = new Object(); 46 47 @GuardedBy("mLock") 48 final Set<SynchronizedCaptureSession> mCaptureSessions = new LinkedHashSet<>(); 49 @GuardedBy("mLock") 50 final Set<SynchronizedCaptureSession> mClosingCaptureSession = new LinkedHashSet<>(); 51 @GuardedBy("mLock") 52 final Set<SynchronizedCaptureSession> mCreatingCaptureSessions = new LinkedHashSet<>(); 53 CaptureSessionRepository(@ameraExecutor @onNull Executor executor)54 CaptureSessionRepository(@CameraExecutor @NonNull Executor executor) { 55 mExecutor = executor; 56 } 57 58 private final CameraDevice.StateCallback mCameraStateCallback = 59 new CameraDevice.StateCallback() { 60 @Override 61 public void onOpened(@NonNull CameraDevice camera) { 62 // Nothing to do. 63 } 64 65 @Override 66 public void onError(@NonNull CameraDevice camera, int error) { 67 // Force close all opened CameraCaptureSessions since the CameraDevice is in 68 // error state. The CameraCaptureSession.close() may not invoke the onClosed() 69 // callback so it has to finish the close process forcibly. 70 forceOnClosedCaptureSessions(); 71 dispatchOnError(error); 72 cameraClosed(); 73 } 74 75 @Override 76 public void onClosed(@NonNull CameraDevice camera) { 77 forceOnClosedCaptureSessions(); 78 cameraClosed(); 79 } 80 81 @Override 82 public void onDisconnected(@NonNull CameraDevice camera) { 83 // Force the onClosed() callback to be made. This is necessary because the 84 // onClosed() callback never gets called if CameraDevice.StateCallback 85 // .onDisconnected() is called. See 86 // TODO(b/140955560) If the issue is fixed then on OS releases with the fix 87 // this should not be called and instead onClosed() should be called by the 88 // framework instead. 89 90 // Force close all opened CameraCaptureSessions since the CameraDevice is 91 // disconnected. 92 // The CameraCaptureSession will call its close() automatically once the 93 // onDisconnected callback is invoked. 94 forceOnClosedCaptureSessions(); 95 cameraClosed(); 96 } 97 98 private void forceOnClosedCaptureSessions() { 99 LinkedHashSet<SynchronizedCaptureSession> sessions = new LinkedHashSet<>(); 100 synchronized (mLock) { 101 sessions.addAll(mCreatingCaptureSessions); 102 sessions.addAll(mCaptureSessions); 103 } 104 mExecutor.execute(() -> forceOnClosed(sessions)); 105 } 106 107 private void dispatchOnError(int error) { 108 LinkedHashSet<SynchronizedCaptureSession> sessions = new LinkedHashSet<>(); 109 synchronized (mLock) { 110 sessions.addAll(mCreatingCaptureSessions); 111 sessions.addAll(mCaptureSessions); 112 } 113 mExecutor.execute(() -> { 114 for (SynchronizedCaptureSession session : sessions) { 115 session.onCameraDeviceError(error); 116 } 117 }); 118 } 119 120 private void cameraClosed() { 121 List<SynchronizedCaptureSession> sessions; 122 synchronized (mLock) { 123 sessions = getSessionsInOrder(); 124 mCreatingCaptureSessions.clear(); 125 mCaptureSessions.clear(); 126 mClosingCaptureSession.clear(); 127 } 128 for (SynchronizedCaptureSession s : sessions) { 129 s.finishClose(); 130 } 131 } 132 }; 133 getCameraStateCallback()134 CameraDevice.@NonNull StateCallback getCameraStateCallback() { 135 return mCameraStateCallback; 136 } 137 forceOnClosed(@onNull Set<SynchronizedCaptureSession> sessions)138 static void forceOnClosed(@NonNull Set<SynchronizedCaptureSession> sessions) { 139 for (SynchronizedCaptureSession session : sessions) { 140 session.getStateCallback().onClosed(session); 141 } 142 } 143 144 /** 145 * Finish close the sessions that are created before the current session. 146 * 147 * @param session the current session that is configured at this moment. 148 */ forceFinishCloseStaleSessions(@onNull SynchronizedCaptureSession session)149 private void forceFinishCloseStaleSessions(@NonNull SynchronizedCaptureSession session) { 150 List<SynchronizedCaptureSession> sessions = getSessionsInOrder(); 151 for (SynchronizedCaptureSession s : sessions) { 152 // Collect the sessions that started configuring before the current session. The 153 // current session and the session that starts configure after the current session 154 // are not included. 155 if (s == session) { 156 break; 157 } 158 s.finishClose(); 159 } 160 } 161 getCaptureSessions()162 @NonNull List<SynchronizedCaptureSession> getCaptureSessions() { 163 synchronized (mLock) { 164 return new ArrayList<>(mCaptureSessions); 165 } 166 } 167 getClosingCaptureSession()168 @NonNull List<SynchronizedCaptureSession> getClosingCaptureSession() { 169 synchronized (mLock) { 170 return new ArrayList<>(mClosingCaptureSession); 171 } 172 } 173 getCreatingCaptureSessions()174 @NonNull List<SynchronizedCaptureSession> getCreatingCaptureSessions() { 175 synchronized (mLock) { 176 return new ArrayList<>(mCreatingCaptureSessions); 177 } 178 } 179 180 /** 181 * Return the session list in the insertion-ordered, It represents the creation order of the 182 * sessions. All the opened sessions (including under closing sessions) are in 183 * getCaptureSessions() and all the opening sessions are in getCreatingCaptureSessions(). The 184 * returned result contains all the opening and opened sessions in the creation-ordered. 185 * 186 * @return the SynchronizedCaptureSession list in the insertion-ordered 187 */ getSessionsInOrder()188 @NonNull List<SynchronizedCaptureSession> getSessionsInOrder() { 189 synchronized (mLock) { 190 List<SynchronizedCaptureSession> sessions = new ArrayList<>(); 191 sessions.addAll(getCaptureSessions()); 192 sessions.addAll(getCreatingCaptureSessions()); 193 return sessions; 194 } 195 } 196 onCreateCaptureSession(@onNull SynchronizedCaptureSession synchronizedCaptureSession)197 void onCreateCaptureSession(@NonNull SynchronizedCaptureSession synchronizedCaptureSession) { 198 synchronized (mLock) { 199 mCreatingCaptureSessions.add(synchronizedCaptureSession); 200 } 201 } 202 onCaptureSessionConfigureFail( @onNull SynchronizedCaptureSession synchronizedCaptureSession)203 void onCaptureSessionConfigureFail( 204 @NonNull SynchronizedCaptureSession synchronizedCaptureSession) { 205 forceFinishCloseStaleSessions(synchronizedCaptureSession); 206 synchronized (mLock) { 207 mCreatingCaptureSessions.remove(synchronizedCaptureSession); 208 } 209 } 210 onCaptureSessionCreated( @onNull SynchronizedCaptureSession synchronizedCaptureSession)211 void onCaptureSessionCreated( 212 @NonNull SynchronizedCaptureSession synchronizedCaptureSession) { 213 synchronized (mLock) { 214 mCaptureSessions.add(synchronizedCaptureSession); 215 mCreatingCaptureSessions.remove(synchronizedCaptureSession); 216 } 217 forceFinishCloseStaleSessions(synchronizedCaptureSession); 218 } 219 onCaptureSessionClosed(@onNull SynchronizedCaptureSession synchronizedCaptureSession)220 void onCaptureSessionClosed(@NonNull SynchronizedCaptureSession synchronizedCaptureSession) { 221 synchronized (mLock) { 222 mCaptureSessions.remove(synchronizedCaptureSession); 223 mClosingCaptureSession.remove(synchronizedCaptureSession); 224 } 225 } 226 onCaptureSessionClosing(@onNull SynchronizedCaptureSession synchronizedCaptureSession)227 void onCaptureSessionClosing(@NonNull SynchronizedCaptureSession synchronizedCaptureSession) { 228 synchronized (mLock) { 229 mClosingCaptureSession.add(synchronizedCaptureSession); 230 } 231 } 232 } 233