diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
index a71aee2f..c74dc0e7 100644
--- a/src/android/camera_device.cpp
+++ b/src/android/camera_device.cpp
@@ -748,27 +748,40 @@ int CameraDevice::open(const hw_module_t *hardwareModule)
 
 void CameraDevice::close()
 {
-	streams_.clear();
-
-	stop();
+	std::scoped_lock<std::mutex> lock(mutex_);
 
-	camera_->release();
+	stop(/*releaseCamera=*/true);
 }
 
-void CameraDevice::stop()
+void CameraDevice::stop(bool releaseCamera)
 {
-	if (!running_)
+	streams_.clear();
+
+	if (!running_) {
+		if (releaseCamera) {
+			descriptors_.clear();
+			camera_->release();
+		}
 		return;
+	}
+
+	stopping_ = true;
 
 	worker_.stop();
 	camera_->stop();
 
 	descriptors_.clear();
+
 	running_ = false;
+	stopping_ = false;
+
+	if (releaseCamera)
+		camera_->release();
 }
 
 void CameraDevice::setCallbacks(const camera3_callback_ops_t *callbacks)
 {
+	std::scoped_lock<std::mutex> lock(mutex_);
 	callbacks_ = callbacks;
 }
 
@@ -1581,6 +1594,8 @@ std::unique_ptr<CameraMetadata> CameraDevice::requestTemplateVideo()
  */
 const camera_metadata_t *CameraDevice::constructDefaultRequestSettings(int type)
 {
+	std::scoped_lock<std::mutex> lock(mutex_);
+
 	auto it = requestTemplates_.find(type);
 	if (it != requestTemplates_.end())
 		return it->second->get();
@@ -1648,8 +1663,7 @@ PixelFormat CameraDevice::toPixelFormat(int format) const
  */
 int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
 {
-	/* Before any configuration attempt, stop the camera. */
-	stop();
+	std::scoped_lock<std::mutex> lock(mutex_);
 
 	if (stream_list->num_streams == 0) {
 		LOG(HAL, Error) << "No streams in configuration";
@@ -1661,6 +1675,9 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
 		return -EINVAL;
 #endif
 
+	/* Before any configuration attempt, stop the camera. */
+	stop();
+
 	/*
 	 * Generate an empty configuration, and construct a StreamConfiguration
 	 * for each camera3_stream to add to it.
@@ -1672,11 +1689,10 @@ int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)
 	}
 
 	/*
-	 * Clear and remove any existing configuration from previous calls, and
-	 * ensure the required entries are available without further
+	 * Ensure the required entries are available without further
 	 * reallocation.
 	 */
-	streams_.clear();
+	ASSERT(streams_.empty());
 	streams_.reserve(stream_list->num_streams);
 
 	std::vector<Camera3StreamConfig> streamConfigs;
@@ -1912,6 +1928,8 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
 	if (!isValidRequest(camera3Request))
 		return -EINVAL;
 
+	std::scoped_lock<std::mutex> lock(mutex_);
+
 	/* Start the camera if that's the first request we handle. */
 	if (!running_) {
 		worker_.start();
@@ -2015,33 +2033,31 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
 
 	worker_.queueRequest(descriptor.request_.get());
 
-	{
-		std::scoped_lock<std::mutex> lock(mutex_);
-		descriptors_[descriptor.request_->cookie()] = std::move(descriptor);
-	}
+	descriptors_[descriptor.request_->cookie()] = std::move(descriptor);
 
 	return 0;
 }
 
 void CameraDevice::requestComplete(Request *request)
 {
+	if (stopping_)
+		return;
+
+	std::scoped_lock<std::mutex> lock(mutex_);
+
 	const Request::BufferMap &buffers = request->buffers();
 	camera3_buffer_status status = CAMERA3_BUFFER_STATUS_OK;
 	std::unique_ptr<CameraMetadata> resultMetadata;
 
-	decltype(descriptors_)::node_type node;
-	{
-		std::scoped_lock<std::mutex> lock(mutex_);
-		auto it = descriptors_.find(request->cookie());
-		if (it == descriptors_.end()) {
-			LOG(HAL, Fatal)
-				<< "Unknown request: " << request->cookie();
-			status = CAMERA3_BUFFER_STATUS_ERROR;
-			return;
-		}
-
-		node = descriptors_.extract(it);
+	auto it = descriptors_.find(request->cookie());
+	if (it == descriptors_.end()) {
+		LOG(HAL, Debug)
+			<< "Unknown request: " << request->cookie();
+		status = CAMERA3_BUFFER_STATUS_ERROR;
+		return;
 	}
+
+	auto node = descriptors_.extract(it);
 	Camera3RequestDescriptor &descriptor = node.mapped();
 
 	if (request->status() != Request::RequestComplete) {
diff --git a/src/android/camera_device.h b/src/android/camera_device.h
index c63e8e21..08553584 100644
--- a/src/android/camera_device.h
+++ b/src/android/camera_device.h
@@ -88,7 +88,7 @@ private:
 		int androidFormat;
 	};
 
-	void stop();
+	void stop(bool releaseCamera = false);
 
 	int initializeStreamConfigurations();
 	std::vector<libcamera::Size>
@@ -127,7 +127,12 @@ private:
 	std::map<int, libcamera::PixelFormat> formatsMap_;
 	std::vector<CameraStream> streams_;
 
-	std::mutex mutex_; /* Protect descriptors_ */
+	/*
+	 * Protect descriptors_ and camera_, and also avoid races by concurrent
+	 * Camera HAL API calls.
+	 */
+	std::mutex mutex_;
+	std::atomic_bool stopping_;
 	std::map<uint64_t, Camera3RequestDescriptor> descriptors_;
 
 	std::string maker_;
