camera: Implement CameraPrivacySwitchMonitor
CameraPrivacySwitchMonitor is used to monitor the camera privacy switch
through v4l2 controls.
BUG=b:167994459
TEST=Build successfully
Disallow-Recycled-Builds: all
Change-Id: Ib43194eae7fd368f59138601c1e63c4dc4baa2b7
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2550973
Tested-by: Jasmine Chen <lnishan@google.com>
Commit-Queue: Jasmine Chen <lnishan@google.com>
Reviewed-by: Shik Chen <shik@chromium.org>
diff --git a/camera/hal/usb/BUILD.gn b/camera/hal/usb/BUILD.gn
index 80c9bc4..3d95845 100644
--- a/camera/hal/usb/BUILD.gn
+++ b/camera/hal/usb/BUILD.gn
@@ -49,6 +49,7 @@
"camera_client.cc",
"camera_hal.cc",
"camera_hal_device_ops.cc",
+ "camera_privacy_switch_monitor.cc",
"capture_request.cc",
"cros_device_config.cc",
"frame_buffer.cc",
diff --git a/camera/hal/usb/camera_client.cc b/camera/hal/usb/camera_client.cc
index ae1986f..83bdacd 100644
--- a/camera/hal/usb/camera_client.cc
+++ b/camera/hal/usb/camera_client.cc
@@ -38,11 +38,12 @@
const camera_metadata_t& static_metadata,
const camera_metadata_t& request_template,
const hw_module_t* module,
- hw_device_t** hw_device)
+ hw_device_t** hw_device,
+ CameraPrivacySwitchMonitor* privacy_switch_monitor)
: id_(id),
device_info_(device_info),
static_metadata_(clone_camera_metadata(&static_metadata)),
- device_(new V4L2CameraDevice(device_info)),
+ device_(new V4L2CameraDevice(device_info, privacy_switch_monitor)),
callback_ops_(nullptr),
request_thread_("Capture request thread") {
memset(&camera3_device_, 0, sizeof(camera3_device_));
diff --git a/camera/hal/usb/camera_client.h b/camera/hal/usb/camera_client.h
index 8b7c457..bdfa091 100644
--- a/camera/hal/usb/camera_client.h
+++ b/camera/hal/usb/camera_client.h
@@ -23,6 +23,7 @@
#include "cros-camera/camera_buffer_manager.h"
#include "cros-camera/future.h"
#include "hal/usb/cached_frame.h"
+#include "hal/usb/camera_privacy_switch_monitor.h"
#include "hal/usb/capture_request.h"
#include "hal/usb/common_types.h"
#include "hal/usb/frame_buffer.h"
@@ -61,10 +62,10 @@
const camera_metadata_t& static_metadata,
const camera_metadata_t& request_template,
const hw_module_t* module,
- hw_device_t** hw_device);
+ hw_device_t** hw_device,
+ CameraPrivacySwitchMonitor* privacy_switch_monitor);
CameraClient(const CameraClient&) = delete;
CameraClient& operator=(const CameraClient&) = delete;
-
~CameraClient();
// Camera Device Operations from CameraHal.
diff --git a/camera/hal/usb/camera_hal.cc b/camera/hal/usb/camera_hal.cc
index 598c2d5..47a4492 100644
--- a/camera/hal/usb/camera_hal.cc
+++ b/camera/hal/usb/camera_hal.cc
@@ -13,7 +13,6 @@
#include <base/threading/thread_task_runner_handle.h>
#include "cros-camera/common.h"
-#include "cros-camera/cros_camera_hal.h"
#include "cros-camera/udev_watcher.h"
#include "hal/usb/camera_characteristics.h"
#include "hal/usb/common_types.h"
@@ -160,9 +159,10 @@
<< cameras_.begin()->first << " is already opened.";
return -EUSERS;
}
- cameras_[id].reset(
- new CameraClient(id, device_infos_[id], *static_metadata_[id].get(),
- *request_template_[id].get(), module, hw_device));
+ cameras_[id].reset(new CameraClient(id, device_infos_[id],
+ *static_metadata_[id].get(),
+ *request_template_[id].get(), module,
+ hw_device, &privacy_switch_monitor_));
if (cameras_[id]->OpenDevice()) {
cameras_.erase(id);
return -ENODEV;
@@ -308,6 +308,11 @@
mojo_manager_ = nullptr;
}
+void CameraHal::SetPrivacySwitchCallback(
+ PrivacySwitchStateChangeCallback callback) {
+ privacy_switch_monitor_.RegisterCallback(std::move(callback));
+}
+
void CameraHal::CloseDeviceOnOpsThread(int id) {
DCHECK(task_runner_);
auto future = cros::Future<void>::Create(nullptr);
@@ -583,6 +588,11 @@
CameraHal::GetInstance().TearDown();
}
+static void set_privacy_switch_callback(
+ PrivacySwitchStateChangeCallback callback) {
+ CameraHal::GetInstance().SetPrivacySwitchCallback(std::move(callback));
+}
+
int camera_device_close(struct hw_device_t* hw_device) {
camera3_device_t* cam_dev = reinterpret_cast<camera3_device_t*>(hw_device);
CameraClient* cam = static_cast<CameraClient*>(cam_dev->priv);
@@ -621,4 +631,6 @@
.reserved = {0}};
cros::cros_camera_hal_t CROS_CAMERA_HAL_INFO_SYM CROS_CAMERA_EXPORT = {
- .set_up = cros::set_up, .tear_down = cros::tear_down};
+ .set_up = cros::set_up,
+ .tear_down = cros::tear_down,
+ .set_privacy_switch_callback = cros::set_privacy_switch_callback};
diff --git a/camera/hal/usb/camera_hal.h b/camera/hal/usb/camera_hal.h
index 0d086b5..c108bc0 100644
--- a/camera/hal/usb/camera_hal.h
+++ b/camera/hal/usb/camera_hal.h
@@ -18,10 +18,12 @@
#include <hardware/camera_common.h>
#include "cros-camera/camera_mojo_channel_manager.h"
+#include "cros-camera/cros_camera_hal.h"
#include "cros-camera/future.h"
#include "cros-camera/udev_watcher.h"
#include "hal/usb/camera_characteristics.h"
#include "hal/usb/camera_client.h"
+#include "hal/usb/camera_privacy_switch_monitor.h"
#include "hal/usb/common_types.h"
#include "hal/usb/cros_device_config.h"
@@ -54,6 +56,7 @@
// Implementations for cros_camera_hal_t.
void SetUp(CameraMojoChannelManager* mojo_manager);
void TearDown();
+ void SetPrivacySwitchCallback(PrivacySwitchStateChangeCallback callback);
// Runs on device ops thread. Post a task to the thread which is used for
// OpenDevice.
@@ -68,6 +71,9 @@
void OnDeviceAdded(ScopedUdevDevicePtr dev) override;
void OnDeviceRemoved(ScopedUdevDevicePtr dev) override;
+ // The monitor for camera privacy switch status changed.
+ CameraPrivacySwitchMonitor privacy_switch_monitor_;
+
// Cache device information because querying the information is very slow.
std::map<int, DeviceInfo> device_infos_;
diff --git a/camera/hal/usb/camera_privacy_switch_monitor.cc b/camera/hal/usb/camera_privacy_switch_monitor.cc
new file mode 100644
index 0000000..cb931a6
--- /dev/null
+++ b/camera/hal/usb/camera_privacy_switch_monitor.cc
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "hal/usb/camera_privacy_switch_monitor.h"
+
+#include <utility>
+
+#include "cros-camera/common.h"
+
+namespace cros {
+
+CameraPrivacySwitchMonitor::CameraPrivacySwitchMonitor()
+ : state_(PrivacySwitchState::kUnknown) {
+ VLOGF_ENTER();
+}
+
+CameraPrivacySwitchMonitor::~CameraPrivacySwitchMonitor() {
+ VLOGF_ENTER();
+}
+
+void CameraPrivacySwitchMonitor::RegisterCallback(
+ PrivacySwitchStateChangeCallback callback) {
+ callback_ = std::move(callback);
+}
+
+void CameraPrivacySwitchMonitor::OnStatusChanged(PrivacySwitchState state) {
+ if (state == state_) {
+ return;
+ }
+
+ state_ = state;
+ if (!callback_.is_null()) {
+ callback_.Run(state);
+ }
+}
+
+} // namespace cros
diff --git a/camera/hal/usb/camera_privacy_switch_monitor.h b/camera/hal/usb/camera_privacy_switch_monitor.h
new file mode 100644
index 0000000..ae40773
--- /dev/null
+++ b/camera/hal/usb/camera_privacy_switch_monitor.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef CAMERA_HAL_USB_CAMERA_PRIVACY_SWITCH_MONITOR_H_
+#define CAMERA_HAL_USB_CAMERA_PRIVACY_SWITCH_MONITOR_H_
+
+#include <memory>
+#include <vector>
+
+#include <base/callback.h>
+
+#include "cros-camera/camera_mojo_channel_manager.h"
+#include "cros-camera/cros_camera_hal.h"
+
+namespace cros {
+
+// CameraPrivacySwitchMonitor is a monitor for the status change of camera
+// privacy switch.
+class CameraPrivacySwitchMonitor final {
+ public:
+ CameraPrivacySwitchMonitor();
+ CameraPrivacySwitchMonitor(const CameraPrivacySwitchMonitor&) = delete;
+ CameraPrivacySwitchMonitor& operator=(const CameraPrivacySwitchMonitor&) =
+ delete;
+ ~CameraPrivacySwitchMonitor();
+
+ void RegisterCallback(PrivacySwitchStateChangeCallback callback);
+
+ void OnStatusChanged(PrivacySwitchState state);
+
+ private:
+ PrivacySwitchState state_;
+
+ PrivacySwitchStateChangeCallback callback_;
+};
+
+} // namespace cros
+
+#endif // CAMERA_HAL_USB_CAMERA_PRIVACY_SWITCH_MONITOR_H_
diff --git a/camera/hal/usb/v4l2_camera_device.cc b/camera/hal/usb/v4l2_camera_device.cc
index cb63cf0..b46b907 100644
--- a/camera/hal/usb/v4l2_camera_device.cc
+++ b/camera/hal/usb/v4l2_camera_device.cc
@@ -88,6 +88,9 @@
case kControlWhiteBalanceTemperature:
return V4L2_CID_WHITE_BALANCE_TEMPERATURE;
+ case kControlPrivacy:
+ return V4L2_CID_PRIVACY;
+
default:
NOTREACHED() << "Unexpected control type " << type;
return -1;
@@ -138,6 +141,9 @@
case kControlWhiteBalanceTemperature:
return "white balance temperature";
+ case kControlPrivacy:
+ return "privacy";
+
default:
NOTREACHED() << "Unexpected control type " << type;
return "N/A";
@@ -197,10 +203,17 @@
} // namespace
V4L2CameraDevice::V4L2CameraDevice()
- : stream_on_(false), device_info_(DeviceInfo()) {}
+ : stream_on_(false),
+ device_info_(DeviceInfo()),
+ event_thread_("V4L2Event") {}
-V4L2CameraDevice::V4L2CameraDevice(const DeviceInfo& device_info)
- : stream_on_(false), device_info_(device_info) {}
+V4L2CameraDevice::V4L2CameraDevice(
+ const DeviceInfo& device_info,
+ CameraPrivacySwitchMonitor* privacy_switch_monitor)
+ : stream_on_(false),
+ device_info_(device_info),
+ event_thread_("V4L2 Event Thread"),
+ privacy_switch_monitor_(privacy_switch_monitor) {}
V4L2CameraDevice::~V4L2CameraDevice() {
device_fd_.reset();
@@ -369,6 +382,12 @@
}
}
+ ret = SubscribePrivacySwitchEvent();
+ if (ret != 0) {
+ LOGF(WARNING) << "Failed to subscribe privacy switch event: "
+ << base::safe_strerror(-ret);
+ }
+
// Initialize the capabilities.
if (device_info_.quirks & kQuirkDisableFrameRateSetting) {
can_update_frame_rate_ = false;
@@ -386,6 +405,14 @@
void V4L2CameraDevice::Disconnect() {
base::AutoLock l(lock_);
stream_on_ = false;
+
+ int ret = UnsubscribePrivacySwitchEvent();
+ if (ret != 0) {
+ LOGF(WARNING) << "Failed to unsubscribe privacy switch event: "
+ << base::safe_strerror(-ret);
+ }
+ privacy_switch_monitor_->OnStatusChanged(PrivacySwitchState::kUnknown);
+
device_fd_.reset();
buffers_at_client_.clear();
}
@@ -500,6 +527,19 @@
fds->push_back(std::move(temp_fds[i]));
}
+ // Query for the initial value of privacy button status while streaming on if
+ // it is supported.
+ if (IsControlSupported(kControlPrivacy)) {
+ int32_t value;
+ if (GetControlValue(kControlPrivacy, &value) == 0) {
+ privacy_switch_monitor_->OnStatusChanged(
+ value != 0 ? PrivacySwitchState::kOn : PrivacySwitchState::kOff);
+ } else {
+ LOGF(ERROR)
+ << "Failed to get the initial status of camera privacy switch";
+ }
+ }
+
stream_on_ = true;
return 0;
}
@@ -1419,4 +1459,77 @@
return device_info_.lens_facing == LensFacing::kExternal;
}
+int V4L2CameraDevice::SubscribePrivacySwitchEvent() {
+ struct v4l2_event_subscription sub = {.type = V4L2_EVENT_CTRL,
+ .id = V4L2_CID_PRIVACY};
+ if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_SUBSCRIBE_EVENT, &sub)) < 0) {
+ LOGF(ERROR) << "Unable to subscribe for privacy status change";
+ return -errno;
+ }
+
+ if (!base::CreatePipe(&cancel_fd_, &cancel_pipe_, true)) {
+ LOGF(ERROR) << "Failed to create the cancelation pipe";
+ return -EINVAL;
+ }
+
+ DCHECK(!event_thread_.IsRunning());
+ if (!event_thread_.Start()) {
+ LOGF(ERROR) << "Failed to start V4L2 event thread";
+ return -EINVAL;
+ }
+
+ event_thread_.task_runner()->PostTask(
+ FROM_HERE, base::BindRepeating(&V4L2CameraDevice::RunDequeueEventsLoop,
+ base::Unretained(this)));
+ return 0;
+}
+
+int V4L2CameraDevice::UnsubscribePrivacySwitchEvent() {
+ DCHECK(event_thread_.IsRunning());
+ cancel_pipe_.reset();
+ event_thread_.Stop();
+
+ struct v4l2_event_subscription sub = {.type = V4L2_EVENT_CTRL,
+ .id = V4L2_CID_PRIVACY};
+ if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_UNSUBSCRIBE_EVENT, &sub)) <
+ 0) {
+ LOGF(ERROR) << "Unable to unsubscribe for privacy status change";
+ return -errno;
+ }
+ return 0;
+}
+
+void V4L2CameraDevice::RunDequeueEventsLoop() {
+ while (true) {
+ struct pollfd fds[2] = {
+ {device_fd_.get(), POLLPRI, 0},
+ {cancel_fd_.get(), POLLHUP, 0},
+ };
+
+ if (HANDLE_EINTR(poll(fds, base::size(fds), -1)) <= 0) {
+ LOGF(ERROR) << "Failed to poll to dequeue events";
+ return;
+ }
+
+ if (fds[1].revents > 0) {
+ cancel_fd_.reset();
+ return;
+ }
+
+ if (fds[0].revents > 0) {
+ struct v4l2_event ev = {};
+ if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQEVENT, &ev)) == 0) {
+ if (ev.type == V4L2_EVENT_CTRL && ev.id == V4L2_CID_PRIVACY &&
+ privacy_switch_monitor_) {
+ privacy_switch_monitor_->OnStatusChanged(
+ ev.u.ctrl.value != 0 ? PrivacySwitchState::kOn
+ : PrivacySwitchState::kOff);
+ }
+ } else {
+ LOGF(ERROR) << "Failed to dequeue event from device";
+ }
+ }
+ }
+}
+
} // namespace cros
diff --git a/camera/hal/usb/v4l2_camera_device.h b/camera/hal/usb/v4l2_camera_device.h
index 89c6a13..a9232eb 100644
--- a/camera/hal/usb/v4l2_camera_device.h
+++ b/camera/hal/usb/v4l2_camera_device.h
@@ -16,8 +16,11 @@
#include <base/files/scoped_file.h>
#include <base/synchronization/lock.h>
+#include <base/threading/thread.h>
+#include "cros-camera/cros_camera_hal.h"
#include "cros-camera/timezone.h"
+#include "hal/usb/camera_privacy_switch_monitor.h"
#include "hal/usb/common_types.h"
namespace cros {
@@ -51,6 +54,7 @@
kControlTilt,
kControlZoom,
kControlWhiteBalanceTemperature,
+ kControlPrivacy,
};
constexpr uint32_t kColorTemperatureAuto = 0;
@@ -60,7 +64,8 @@
class V4L2CameraDevice {
public:
V4L2CameraDevice();
- explicit V4L2CameraDevice(const DeviceInfo& device_info);
+ V4L2CameraDevice(const DeviceInfo& device_info,
+ CameraPrivacySwitchMonitor* privacy_switch_monitor);
V4L2CameraDevice(const V4L2CameraDevice&) = delete;
V4L2CameraDevice& operator=(const V4L2CameraDevice&) = delete;
@@ -237,6 +242,17 @@
// Returns true if the current connected device is an external camera.
bool IsExternalCamera();
+ // Subscribe the camera privacy switch status changed as privacy v4l2-event.
+ // Returns |-errno| if it fails to subscribe.
+ int SubscribePrivacySwitchEvent();
+
+ // Unsubscribe the camera privacy switch status changed as privacy v4l2-event.
+ // Returns |-errno| if it fails to unsubscribe.
+ int UnsubscribePrivacySwitchEvent();
+
+ // Keep dequeuing the v4l2-events from device.
+ void RunDequeueEventsLoop();
+
// The number of video buffers we want to request in kernel.
const int kNumVideoBuffers = 4;
@@ -267,6 +283,20 @@
// Current control values.
std::map<ControlType, int32_t> control_values_;
+ // The thread for dequeing v4l2-events.
+ base::Thread event_thread_;
+
+ // The endpoint of cancelation pipe. The main thread should close it before
+ // trying to stop the event thread.
+ base::ScopedFD cancel_pipe_;
+
+ // The endpoint of cancelation pipe. The event thread should poll for it so
+ // that we can notify the thread to leave the loop.
+ base::ScopedFD cancel_fd_;
+
+ // Monitor for the status change of camera privacy switch.
+ CameraPrivacySwitchMonitor* privacy_switch_monitor_;
+
// Since V4L2CameraDevice may be called on different threads, this is used to
// guard all variables.
base::Lock lock_;
diff --git a/camera/hal_adapter/camera_hal_server_impl.cc b/camera/hal_adapter/camera_hal_server_impl.cc
index 2da9b94..2fbd0ae 100644
--- a/camera/hal_adapter/camera_hal_server_impl.cc
+++ b/camera/hal_adapter/camera_hal_server_impl.cc
@@ -64,7 +64,18 @@
mojo_manager_->GetIpcTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&CameraHalServerImpl::IPCBridge::Start,
- ipc_bridge_->GetWeakPtr(), camera_hal_adapter_.get()));
+ ipc_bridge_->GetWeakPtr(), camera_hal_adapter_.get(),
+ base::BindRepeating(
+ [](const std::vector<cros_camera_hal_t*>& hals,
+ PrivacySwitchStateChangeCallback callback) {
+ for (const auto* hal : hals) {
+ if (hal->set_privacy_switch_callback != nullptr) {
+ hal->set_privacy_switch_callback(
+ std::move(callback));
+ }
+ }
+ },
+ cros_camera_hals_)));
return true;
}
@@ -85,7 +96,8 @@
}
void CameraHalServerImpl::IPCBridge::Start(
- CameraHalAdapter* camera_hal_adapter) {
+ CameraHalAdapter* camera_hal_adapter,
+ SetPrivacySwitchCallback set_privacy_switch_callback) {
VLOGF_ENTER();
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
@@ -103,7 +115,7 @@
mojo_manager_->RegisterServer(
std::move(server_ptr),
base::BindOnce(&CameraHalServerImpl::IPCBridge::OnServerRegistered,
- GetWeakPtr()),
+ GetWeakPtr(), std::move(set_privacy_switch_callback)),
base::BindOnce(&CameraHalServerImpl::IPCBridge::OnServiceMojoChannelError,
GetWeakPtr()));
}
@@ -140,7 +152,9 @@
}
void CameraHalServerImpl::IPCBridge::OnServerRegistered(
- int32_t result, mojom::CameraHalServerCallbacksPtr callbacks) {
+ SetPrivacySwitchCallback set_privacy_switch_callback,
+ int32_t result,
+ mojom::CameraHalServerCallbacksPtr callbacks) {
VLOGF_ENTER();
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
@@ -150,6 +164,12 @@
return;
}
callbacks_.Bind(callbacks.PassInterface());
+
+ std::move(set_privacy_switch_callback)
+ .Run(base::BindRepeating(
+ &CameraHalServerImpl::IPCBridge::OnPrivacySwitchStatusChanged,
+ base::Unretained(this)));
+
LOGF(INFO) << "Registered camera HAL";
}
@@ -165,6 +185,19 @@
base::Unretained(camera_hal_server_), ECONNRESET));
}
+void CameraHalServerImpl::IPCBridge::OnPrivacySwitchStatusChanged(
+ PrivacySwitchState state) {
+ cros::mojom::CameraPrivacySwitchState state_in_mojo;
+ if (state == PrivacySwitchState::kUnknown) {
+ state_in_mojo = cros::mojom::CameraPrivacySwitchState::UNKNOWN;
+ } else if (state == PrivacySwitchState::kOn) {
+ state_in_mojo = cros::mojom::CameraPrivacySwitchState::ON;
+ } else { // state == PrivacySwitchState::kOff
+ state_in_mojo = cros::mojom::CameraPrivacySwitchState::OFF;
+ }
+ callbacks_->CameraPrivacySwitchStateChange(state_in_mojo);
+}
+
void CameraHalServerImpl::LoadCameraHal() {
VLOGF_ENTER();
DCHECK(!camera_hal_adapter_);
diff --git a/camera/hal_adapter/camera_hal_server_impl.h b/camera/hal_adapter/camera_hal_server_impl.h
index 0d3f778..31db2cd 100644
--- a/camera/hal_adapter/camera_hal_server_impl.h
+++ b/camera/hal_adapter/camera_hal_server_impl.h
@@ -42,6 +42,9 @@
bool Start();
private:
+ using SetPrivacySwitchCallback =
+ base::OnceCallback<void(PrivacySwitchStateChangeCallback)>;
+
// IPCBridge wraps all the IPC-related calls. Most of its methods should/will
// be run on IPC thread.
class IPCBridge : public mojom::CameraHalServer {
@@ -51,7 +54,8 @@
~IPCBridge();
- void Start(CameraHalAdapter* camera_hal_adapter);
+ void Start(CameraHalAdapter* camera_hal_adapter,
+ SetPrivacySwitchCallback set_privacy_switch_callback);
// CameraHalServer Mojo interface implementation.
@@ -70,12 +74,17 @@
private:
// Triggered when the HAL server is registered.
- void OnServerRegistered(int32_t result,
- mojom::CameraHalServerCallbacksPtr callbacks);
+ void OnServerRegistered(
+ SetPrivacySwitchCallback set_privacy_switch_callback,
+ int32_t result,
+ mojom::CameraHalServerCallbacksPtr callbacks);
// Connection error handler for the Mojo connection to CameraHalDispatcher.
void OnServiceMojoChannelError();
+ // Triggers when the camera privacy switch status changed.
+ void OnPrivacySwitchStatusChanged(PrivacySwitchState state);
+
CameraHalServerImpl* camera_hal_server_;
CameraMojoChannelManager* mojo_manager_;
diff --git a/camera/include/cros-camera/cros_camera_hal.h b/camera/include/cros-camera/cros_camera_hal.h
index 6fe5957..3d1d01a 100644
--- a/camera/include/cros-camera/cros_camera_hal.h
+++ b/camera/include/cros-camera/cros_camera_hal.h
@@ -14,6 +14,15 @@
namespace cros {
+enum class PrivacySwitchState {
+ kUnknown,
+ kOn,
+ kOff,
+};
+
+using PrivacySwitchStateChangeCallback =
+ base::RepeatingCallback<void(PrivacySwitchState state)>;
+
typedef struct cros_camera_hal {
/**
* Sets up the camera HAL. The |mojo_manager| can be used for communication
@@ -26,8 +35,14 @@
*/
void (*tear_down)();
+ /**
+ * Registers camera privacy switch observer.
+ */
+ void (*set_privacy_switch_callback)(
+ PrivacySwitchStateChangeCallback callback);
+
/* reserved for future use */
- void* reserved[5];
+ void* reserved[4];
} cros_camera_hal_t;
} // namespace cros
diff --git a/camera/mojo/cros_camera_service.mojom b/camera/mojo/cros_camera_service.mojom
index 9829be4..12fe2c4 100644
--- a/camera/mojo/cros_camera_service.mojom
+++ b/camera/mojo/cros_camera_service.mojom
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Next min version: 5
+// Next min version: 6
module cros.mojom;
@@ -21,6 +21,22 @@
PLUGINVM = 4,
};
+// CameraPrivacySwitchState indicates the state of the camera privacy switch.
+enum CameraPrivacySwitchState{
+ // For devices which can only read the privacy switch status while the camera
+ // is streaming, it is possible that the state of privacy switch is currently
+ // unknown.
+ UNKNOWN = 0,
+
+ // State when the privacy switch is on, which means the black frames will be
+ // delivered when streaming.
+ ON = 1,
+
+ // State when the privacy switch is off, which means camera should stream
+ // normally.
+ OFF = 2,
+};
+
// The CrOS camera HAL v3 Mojo dispatcher. The dispatcher acts as a proxy and
// waits for the server and the clients to register. There can only be one
// server registered, with multiple clients requesting connections to the
@@ -87,7 +103,7 @@
// CameraHalDispatcher for any changes on the server side, for example when a
// CameraHalClient opens or closes a camera device.
//
-// Next method ID: 1
+// Next method ID: 2
interface CameraHalServerCallbacks {
// Fired when a CameraHalClient opens or closes a camera device. When a
// CameraHalClient loses mojo connection to CameraHalServer, CameraHalServer
@@ -95,6 +111,12 @@
CameraDeviceActivityChange@0(int32 camera_id,
bool opened,
CameraClientType type);
+
+ // Fired when the camera privacy switch status is changed. If the device has
+ // such switch, this callback will be fired immediately for once to notify its
+ // current status when the callbacks are registered.
+ [MinVersion=5]
+ CameraPrivacySwitchStateChange@1(CameraPrivacySwitchState state);
};
// The CrOS camera HAL v3 Mojo client.