diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
index d918350..a79bb69 100644
--- a/src/android/camera_device.cpp
+++ b/src/android/camera_device.cpp
@@ -233,6 +233,21 @@ CameraDevice::~CameraDevice()
 		delete it.second;
 }
 
+std::shared_ptr<CameraDevice> CameraDevice::create(unsigned int id,
+						    const std::shared_ptr<Camera> &cam)
+{
+	struct Deleter : std::default_delete<CameraDevice> {
+		void operator()(CameraDevice *camera)
+		{
+			delete camera;
+		}
+	};
+
+	CameraDevice *camera = new CameraDevice(id, cam);
+
+	return std::shared_ptr<CameraDevice>(camera, Deleter());
+}
+
 /*
  * Initialize the camera static information.
  * This method is called before the camera device is opened.
diff --git a/src/android/camera_device.h b/src/android/camera_device.h
index 7be9e11..7f9e010 100644
--- a/src/android/camera_device.h
+++ b/src/android/camera_device.h
@@ -47,8 +47,8 @@ struct CameraStream {
 class CameraDevice : protected libcamera::Loggable
 {
 public:
-	CameraDevice(unsigned int id, const std::shared_ptr<libcamera::Camera> &camera);
-	~CameraDevice();
+	static std::shared_ptr<CameraDevice> create(unsigned int id,
+						    const std::shared_ptr<libcamera::Camera> &cam);
 
 	int initialize();
 
@@ -57,6 +57,7 @@ public:
 
 	unsigned int id() const { return id_; }
 	camera3_device_t *camera3Device() { return &camera3Device_; }
+	const libcamera::Camera *getCamera() { return camera_.get(); };
 
 	int facing() const { return facing_; }
 	int orientation() const { return orientation_; }
@@ -72,6 +73,9 @@ protected:
 	std::string logPrefix() const override;
 
 private:
+	CameraDevice(unsigned int id, const std::shared_ptr<libcamera::Camera> &camera);
+	~CameraDevice();
+
 	struct Camera3RequestDescriptor {
 		Camera3RequestDescriptor(unsigned int frameNumber,
 					 unsigned int numBuffers);
diff --git a/src/android/camera_hal_manager.cpp b/src/android/camera_hal_manager.cpp
index 3d6d2b4..fdde2c0 100644
--- a/src/android/camera_hal_manager.cpp
+++ b/src/android/camera_hal_manager.cpp
@@ -8,6 +8,7 @@
 #include "camera_hal_manager.h"
 
 #include <libcamera/camera.h>
+#include <libcamera/property_ids.h>
 
 #include "libcamera/internal/log.h"
 
@@ -35,6 +36,7 @@ CameraHalManager::CameraHalManager()
 CameraHalManager::~CameraHalManager()
 {
 	cameras_.clear();
+	camerasMap_.clear();
 
 	if (cameraManager_) {
 		cameraManager_->stop();
@@ -47,6 +49,13 @@ int CameraHalManager::init()
 {
 	cameraManager_ = new CameraManager();
 
+	/* Support camera hotplug. */
+	cameraManager_->cameraAdded.connect(this, &CameraHalManager::cameraAdded);
+	cameraManager_->cameraRemoved.connect(this, &CameraHalManager::cameraRemoved);
+
+	cameraCounter_ = 0;
+	externalCameraCounter_ = 1000;
+
 	int ret = cameraManager_->start();
 	if (ret) {
 		LOG(HAL, Error) << "Failed to start camera manager: "
@@ -56,35 +65,25 @@ int CameraHalManager::init()
 		return ret;
 	}
 
-	/*
-	 * For each Camera registered in the system, a CameraDevice
-	 * gets created here to wraps a libcamera Camera instance.
-	 *
-	 * \todo Support camera hotplug.
-	 */
-	unsigned int index = 0;
-	for (auto &cam : cameraManager_->cameras()) {
-		CameraDevice *camera = new CameraDevice(index, cam);
-		ret = camera->initialize();
-		if (ret)
-			continue;
-
-		cameras_.emplace_back(camera);
-		++index;
-	}
-
 	return 0;
 }
 
 CameraDevice *CameraHalManager::open(unsigned int id,
 				     const hw_module_t *hardwareModule)
 {
-	if (id >= numCameras()) {
+	MutexLocker locker(mutex_);
+
+	auto iter = std::find_if(cameras_.begin(), cameras_.end(),
+				 [id](std::shared_ptr<CameraDevice> &cam) {
+					return cam->id() == id;
+				});
+	if (iter == cameras_.end()) {
 		LOG(HAL, Error) << "Invalid camera id '" << id << "'";
 		return nullptr;
 	}
 
-	CameraDevice *camera = cameras_[id].get();
+	CameraDevice *camera = iter->get();
+
 	if (camera->open(hardwareModule))
 		return nullptr;
 
@@ -93,9 +92,91 @@ CameraDevice *CameraHalManager::open(unsigned int id,
 	return camera;
 }
 
+void CameraHalManager::cameraAdded(std::shared_ptr<Camera> cam)
+{
+	unsigned int id;
+	bool isCameraExternal = false;
+	bool isCameraNew = false;
+
+	MutexLocker locker(mutex_);
+
+	/* Each camera is assigned a unique integer id when it is seen for the
+	 * first time. If the camera has been seen before, the id is reused and
+	 * the camera is marked as CAMERA_DEVICE_STATUS_PRESENT subsequently.
+	 *
+	 * ID starts from '0' for internal cameras and '1000' for external cameras.
+	 */
+	auto iter = camerasMap_.find(cam->id());
+	if (iter != camerasMap_.end()) {
+		id = iter->second;
+	} else {
+		isCameraNew = true;
+
+		/*
+		 *  Now check if this is an external camera and assign
+		 *  its id accordingly.
+		 */
+		const ControlList &properties = cam->properties();
+		if (properties.contains(properties::Location) &&
+		    properties.get(properties::Location) &
+		    properties::CameraLocationExternal) {
+			isCameraExternal = true;
+			id = externalCameraCounter_;
+		} else {
+			id = cameraCounter_;
+		}
+	}
+
+	/*
+	 * For each Camera registered in the system, a CameraDevice
+	 * gets created here to wraps a libcamera Camera instance.
+	 */
+	std::shared_ptr<CameraDevice> camera = CameraDevice::create(id, std::move(cam));
+	int ret = camera->initialize();
+	if (ret) {
+		LOG(HAL, Error) << "Failed to initialize camera: " << cam->id();
+		return;
+	}
+
+	if (isCameraNew) {
+		camerasMap_.emplace(cam->id(), id);
+
+		if (isCameraExternal)
+			externalCameraCounter_++;
+		else
+			cameraCounter_++;
+	}
+
+	cameras_.emplace_back(std::move(camera));
+
+	if (callbacks_)
+		callbacks_->camera_device_status_change(callbacks_, id,
+							CAMERA_DEVICE_STATUS_PRESENT);
+	LOG(HAL, Debug) << "Camera ID: " << id << " added successfully.";
+}
+
+void CameraHalManager::cameraRemoved(std::shared_ptr<Camera> cam)
+{
+	MutexLocker locker(mutex_);
+
+	auto iter = std::find_if(cameras_.begin(), cameras_.end(),
+				 [cam](std::shared_ptr<CameraDevice> &camera) {
+					return cam.get() == camera->getCamera();
+				});
+	if (iter == cameras_.end())
+		return;
+
+	unsigned int id = (*iter)->id();
+	callbacks_->camera_device_status_change(callbacks_, id,
+						CAMERA_DEVICE_STATUS_NOT_PRESENT);
+	cameras_.erase(iter);
+
+	LOG(HAL, Debug) << "Camera ID: " << id << " removed successfully.";
+}
+
 unsigned int CameraHalManager::numCameras() const
 {
-	return cameraManager_->cameras().size();
+	return cameraCounter_;
 }
 
 int CameraHalManager::getCameraInfo(unsigned int id, struct camera_info *info)
@@ -103,12 +184,18 @@ int CameraHalManager::getCameraInfo(unsigned int id, struct camera_info *info)
 	if (!info)
 		return -EINVAL;
 
-	if (id >= numCameras()) {
+	MutexLocker locker(mutex_);
+
+	auto iter = std::find_if(cameras_.begin(), cameras_.end(),
+				 [id](std::shared_ptr<CameraDevice> &cam) {
+					return cam->id() == id;
+				});
+	if (iter == cameras_.end()) {
 		LOG(HAL, Error) << "Invalid camera id '" << id << "'";
 		return -EINVAL;
 	}
 
-	CameraDevice *camera = cameras_[id].get();
+	CameraDevice *camera = iter->get();
 
 	info->facing = camera->facing();
 	info->orientation = camera->orientation();
@@ -124,4 +211,26 @@ int CameraHalManager::getCameraInfo(unsigned int id, struct camera_info *info)
 void CameraHalManager::setCallbacks(const camera_module_callbacks_t *callbacks)
 {
 	callbacks_ = callbacks;
+
+	MutexLocker locker(mutex_);
+	/*
+	 * Few cameras might have been hotplugged before setting callbacks_ here.
+	 * We need to mark CAMERA_DEVICE_STATUS_PRESENT for them explicitly.
+	 * This hold only for external cameras, as internal cameras are assumed to
+	 * be present at module load time, by the framework.
+	 */
+	for (auto &cam : cameraManager_->cameras()) {
+		auto iter = camerasMap_.find(cam->id());
+		if (iter == camerasMap_.end())
+			continue;
+
+		unsigned int id = iter->second;
+		const ControlList &properties = cam->properties();
+		if (properties.contains(properties::Location) &&
+		    properties.get(properties::Location) &
+		    properties::CameraLocationExternal) {
+			callbacks_->camera_device_status_change(callbacks_, id,
+								CAMERA_DEVICE_STATUS_PRESENT);
+		}
+	}
 }
diff --git a/src/android/camera_hal_manager.h b/src/android/camera_hal_manager.h
index a582f04..7c481d4 100644
--- a/src/android/camera_hal_manager.h
+++ b/src/android/camera_hal_manager.h
@@ -7,6 +7,8 @@
 #ifndef __ANDROID_CAMERA_MANAGER_H__
 #define __ANDROID_CAMERA_MANAGER_H__
 
+#include <map>
+#include <mutex>
 #include <stddef.h>
 #include <vector>
 
@@ -18,6 +20,9 @@
 
 class CameraDevice;
 
+using Mutex = std::mutex;
+using MutexLocker = std::unique_lock<std::mutex>;
+
 class CameraHalManager
 {
 public:
@@ -33,10 +38,18 @@ public:
 	void setCallbacks(const camera_module_callbacks_t *callbacks);
 
 private:
+	void cameraAdded(std::shared_ptr<libcamera::Camera> cam);
+	void cameraRemoved(std::shared_ptr<libcamera::Camera> cam);
+
 	libcamera::CameraManager *cameraManager_;
 
 	const camera_module_callbacks_t *callbacks_;
-	std::vector<std::unique_ptr<CameraDevice>> cameras_;
+	std::vector<std::shared_ptr<CameraDevice>> cameras_;
+	std::map<std::string, unsigned int> camerasMap_;
+	Mutex mutex_;
+
+	unsigned int externalCameraCounter_;
+	unsigned int cameraCounter_;
 };
 
 #endif /* __ANDROID_CAMERA_MANAGER_H__ */
