Commit d016df33 authored by Wenjie Shi's avatar Wenjie Shi Committed by Commit Bot

Implement PlatformSensorReaderWinrt and

PlatformSensorReaderWinrtLightSensor

As the third step for implementing usage of the
Windows.Devices.Sensors WinRT API in Chromium, this CL:
  - Implements PlatformSensorReaderWinrtBase, the base class
    used to implement all of the Windows.Devices.Sensors based
    PlatformSensorReaders.
  - Implements PlatformSensorReaderWinrtLightSensor, the derived
    class of PlatformSensorReaderWinrtBase which adds support
    for light sensors using Windows.Devices.Sensors.LightSensor.
There is heavy use of templates since the Windows.Devices.Sensors
API is strongly typed.

Verified functionally by:
  - Built chrome.exe.
  - Ran chrome.exe on laptop with light sensor.
  - Navigated to website that polls the W3C light sensor:
    - Verified light sensor could be polled with WinRT feature
      flag on and off.
  - Ran chrome.exe on laptop without light sensor.
  - Navigated to website that polls the W3C light sensor:
    - Verified the light sensor failed to instantiate with the
      WinRT feature flag on and off.
Also ran new PlatformSensorReaderTestWinrt unit tests.

Bug: 958266
Change-Id: Iaa69d92f9e7923099653b4f554efc2852162b53f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1703327
Commit-Queue: Wenjie Shi <wensh@microsoft.com>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#683005}
parent b4581ba3
...@@ -87,6 +87,14 @@ ...@@ -87,6 +87,14 @@
#include "base/win/windows_types.h" #include "base/win/windows_types.h"
#endif #endif
namespace ABI {
namespace Windows {
namespace Foundation {
struct DateTime;
} // namespace Foundation
} // namespace Windows
} // namespace ABI
namespace base { namespace base {
class PlatformThreadHandle; class PlatformThreadHandle;
...@@ -126,7 +134,10 @@ class BASE_EXPORT TimeDelta { ...@@ -126,7 +134,10 @@ class BASE_EXPORT TimeDelta {
static constexpr TimeDelta FromNanosecondsD(double ns); static constexpr TimeDelta FromNanosecondsD(double ns);
#if defined(OS_WIN) #if defined(OS_WIN)
static TimeDelta FromQPCValue(LONGLONG qpc_value); static TimeDelta FromQPCValue(LONGLONG qpc_value);
// TODO(crbug.com/989694): Avoid base::TimeDelta factory functions
// based on absolute time
static TimeDelta FromFileTime(FILETIME ft); static TimeDelta FromFileTime(FILETIME ft);
static TimeDelta FromWinrtDateTime(ABI::Windows::Foundation::DateTime dt);
#elif defined(OS_POSIX) || defined(OS_FUCHSIA) #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
static TimeDelta FromTimeSpec(const timespec& ts); static TimeDelta FromTimeSpec(const timespec& ts);
#endif #endif
...@@ -188,6 +199,9 @@ class BASE_EXPORT TimeDelta { ...@@ -188,6 +199,9 @@ class BASE_EXPORT TimeDelta {
#if defined(OS_FUCHSIA) #if defined(OS_FUCHSIA)
zx_duration_t ToZxDuration() const; zx_duration_t ToZxDuration() const;
#endif #endif
#if defined(OS_WIN)
ABI::Windows::Foundation::DateTime ToWinrtDateTime() const;
#endif
// Returns the time delta in some unit. The InXYZF versions return a floating // Returns the time delta in some unit. The InXYZF versions return a floating
// point value. The InXYZ versions return a truncated value (aka rounded // point value. The InXYZ versions return a truncated value (aka rounded
......
...@@ -31,9 +31,9 @@ ...@@ -31,9 +31,9 @@
// will only increase the system-wide timer if we're not running on battery // will only increase the system-wide timer if we're not running on battery
// power. // power.
#include "base/feature_list.h"
#include "base/time/time.h" #include "base/time/time.h"
#include <windows.foundation.h>
#include <windows.h> #include <windows.h>
#include <mmsystem.h> #include <mmsystem.h>
#include <stdint.h> #include <stdint.h>
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include "base/atomicops.h" #include "base/atomicops.h"
#include "base/bit_cast.h" #include "base/bit_cast.h"
#include "base/cpu.h" #include "base/cpu.h"
#include "base/feature_list.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "base/threading/platform_thread.h" #include "base/threading/platform_thread.h"
...@@ -761,4 +762,16 @@ TimeDelta TimeDelta::FromFileTime(FILETIME ft) { ...@@ -761,4 +762,16 @@ TimeDelta TimeDelta::FromFileTime(FILETIME ft) {
return TimeDelta::FromMicroseconds(FileTimeToMicroseconds(ft)); return TimeDelta::FromMicroseconds(FileTimeToMicroseconds(ft));
} }
// static
TimeDelta TimeDelta::FromWinrtDateTime(ABI::Windows::Foundation::DateTime dt) {
// UniversalTime is 100 ns intervals since January 1, 1601 (UTC)
return TimeDelta::FromMicroseconds(dt.UniversalTime / 10);
}
ABI::Windows::Foundation::DateTime TimeDelta::ToWinrtDateTime() const {
ABI::Windows::Foundation::DateTime date_time;
date_time.UniversalTime = InMicroseconds() * 10;
return date_time;
}
} // namespace base } // namespace base
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <mmsystem.h> #include <mmsystem.h>
#include <process.h> #include <process.h>
#include <stdint.h> #include <stdint.h>
#include <windows.foundation.h>
#include <cmath> #include <cmath>
#include <limits> #include <limits>
...@@ -366,6 +367,32 @@ TEST(TimeDelta, FromFileTime) { ...@@ -366,6 +367,32 @@ TEST(TimeDelta, FromFileTime) {
TimeDelta::FromFileTime(ft)); TimeDelta::FromFileTime(ft));
} }
TEST(TimeDelta, FromWinrtDateTime) {
ABI::Windows::Foundation::DateTime dt;
dt.UniversalTime = 0;
// 0 UniversalTime = no delta since epoch.
EXPECT_EQ(TimeDelta(), TimeDelta::FromWinrtDateTime(dt));
dt.UniversalTime = 101;
// 101 * 100 ns ~= 10.1 microseconds.
EXPECT_EQ(TimeDelta::FromMicrosecondsD(10.1),
TimeDelta::FromWinrtDateTime(dt));
}
TEST(TimeDelta, ToWinrtDateTime) {
auto time_delta = TimeDelta::FromSeconds(0);
// No delta since epoch = 0 DateTime.
EXPECT_EQ(0, time_delta.ToWinrtDateTime().UniversalTime);
time_delta = TimeDelta::FromMicrosecondsD(10);
// 10 microseconds = 100 * 100 ns.
EXPECT_EQ(100, time_delta.ToWinrtDateTime().UniversalTime);
}
TEST(HighResolutionTimer, GetUsage) { TEST(HighResolutionTimer, GetUsage) {
EXPECT_EQ(0.0, Time::GetHighResolutionTimerUsage()); EXPECT_EQ(0.0, Time::GetHighResolutionTimerUsage());
......
...@@ -271,7 +271,10 @@ source_set("tests") { ...@@ -271,7 +271,10 @@ source_set("tests") {
} }
if (is_win) { if (is_win) {
sources += [ "generic_sensor/platform_sensor_provider_winrt_unittest.cc" ] sources += [
"generic_sensor/platform_sensor_provider_winrt_unittest.cc",
"generic_sensor/platform_sensor_reader_winrt_unittests.cc",
]
# Needed for "generic_sensor/platform_sensor_and_provider_unittest_win.cc" # Needed for "generic_sensor/platform_sensor_and_provider_unittest_win.cc"
libs = [ libs = [
......
...@@ -4,14 +4,308 @@ ...@@ -4,14 +4,308 @@
#include "services/device/generic_sensor/platform_sensor_reader_winrt.h" #include "services/device/generic_sensor/platform_sensor_reader_winrt.h"
#include "base/win/core_winrt_util.h"
#include "services/device/public/cpp/generic_sensor/sensor_reading.h"
#include "services/device/public/mojom/sensor.mojom.h" #include "services/device/public/mojom/sensor.mojom.h"
namespace device { namespace device {
namespace {
using ABI::Windows::Devices::Sensors::ILightSensor;
using ABI::Windows::Devices::Sensors::ILightSensorReading;
using ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs;
using ABI::Windows::Devices::Sensors::ILightSensorStatics;
using ABI::Windows::Devices::Sensors::LightSensor;
using ABI::Windows::Devices::Sensors::LightSensorReadingChangedEventArgs;
using ABI::Windows::Foundation::DateTime;
using ABI::Windows::Foundation::ITypedEventHandler;
using Microsoft::WRL::Callback;
using Microsoft::WRL::ComPtr;
} // namespace
// static // static
std::unique_ptr<PlatformSensorReaderWinBase> std::unique_ptr<PlatformSensorReaderWinBase>
PlatformSensorReaderWinrtFactory::Create(mojom::SensorType) { PlatformSensorReaderWinrtFactory::Create(mojom::SensorType type) {
return nullptr; switch (type) {
case mojom::SensorType::AMBIENT_LIGHT:
return PlatformSensorReaderWinrtLightSensor::Create();
default:
NOTIMPLEMENTED();
return nullptr;
}
}
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
PlatformSensorReaderWinrtBase<
runtime_class_id,
ISensorWinrtStatics,
ISensorWinrtClass,
ISensorReadingChangedHandler,
ISensorReadingChangedEventArgs>::PlatformSensorReaderWinrtBase() {
get_sensor_factory_callback_ =
base::Bind([](ISensorWinrtStatics** sensor_factory) -> HRESULT {
return base::win::GetActivationFactory<ISensorWinrtStatics,
runtime_class_id>(
sensor_factory);
});
}
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
void PlatformSensorReaderWinrtBase<
runtime_class_id,
ISensorWinrtStatics,
ISensorWinrtClass,
ISensorReadingChangedHandler,
ISensorReadingChangedEventArgs>::SetClient(Client* client) {
base::AutoLock autolock(lock_);
client_ = client;
}
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
template <class ISensorReading>
HRESULT PlatformSensorReaderWinrtBase<runtime_class_id,
ISensorWinrtStatics,
ISensorWinrtClass,
ISensorReadingChangedHandler,
ISensorReadingChangedEventArgs>::
ConvertSensorReadingTimeStamp(ComPtr<ISensorReading> sensor_reading,
base::TimeDelta* timestamp_delta) {
DateTime timestamp;
HRESULT hr = sensor_reading->get_Timestamp(&timestamp);
if (FAILED(hr))
return hr;
*timestamp_delta = base::TimeDelta::FromWinrtDateTime(timestamp);
return S_OK;
}
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
SensorWinrtCreateFailure
PlatformSensorReaderWinrtBase<runtime_class_id,
ISensorWinrtStatics,
ISensorWinrtClass,
ISensorReadingChangedHandler,
ISensorReadingChangedEventArgs>::Initialize() {
ComPtr<ISensorWinrtStatics> sensor_statics;
HRESULT hr = get_sensor_factory_callback_.Run(&sensor_statics);
if (FAILED(hr))
return SensorWinrtCreateFailure::kErrorISensorWinrtStaticsActivationFailed;
if (FAILED(sensor_statics->GetDefault(&sensor_)))
return SensorWinrtCreateFailure::kErrorGetDefaultSensorFailed;
// GetDefault() returns null if the sensor does not exist
if (!sensor_)
return SensorWinrtCreateFailure::kErrorDefaultSensorNull;
minimum_report_interval_ = GetMinimumReportIntervalFromSensor();
if (minimum_report_interval_.is_zero())
return SensorWinrtCreateFailure::kErrorGetMinReportIntervalFailed;
return SensorWinrtCreateFailure::kOk;
}
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
base::TimeDelta PlatformSensorReaderWinrtBase<
runtime_class_id,
ISensorWinrtStatics,
ISensorWinrtClass,
ISensorReadingChangedHandler,
ISensorReadingChangedEventArgs>::GetMinimumReportIntervalFromSensor() {
UINT32 minimum_report_interval_ms = 0;
HRESULT hr = sensor_->get_MinimumReportInterval(&minimum_report_interval_ms);
// Failing to query is not fatal, consumer should be able to gracefully
// handle a 0 return.
if (FAILED(hr)) {
DLOG(WARNING) << "Failed to query sensor minimum report interval: "
<< logging::SystemErrorCodeToString(hr);
return base::TimeDelta();
}
return base::TimeDelta::FromMilliseconds(minimum_report_interval_ms);
}
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
base::TimeDelta PlatformSensorReaderWinrtBase<
runtime_class_id,
ISensorWinrtStatics,
ISensorWinrtClass,
ISensorReadingChangedHandler,
ISensorReadingChangedEventArgs>::GetMinimalReportingInterval() const {
return minimum_report_interval_;
}
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
bool PlatformSensorReaderWinrtBase<runtime_class_id,
ISensorWinrtStatics,
ISensorWinrtClass,
ISensorReadingChangedHandler,
ISensorReadingChangedEventArgs>::
StartSensor(const PlatformSensorConfiguration& configuration) {
base::AutoLock autolock(lock_);
if (!reading_callback_token_) {
// Convert from frequency to interval in milliseconds since that is
// what the Windows.Devices.Sensors API uses.
unsigned int interval =
(1 / configuration.frequency()) * base::Time::kMillisecondsPerSecond;
auto hr = sensor_->put_ReportInterval(interval);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to set report interval: "
<< logging::SystemErrorCodeToString(hr);
return false;
}
auto reading_changed_handler = Callback<ISensorReadingChangedHandler>(
this, &PlatformSensorReaderWinrtBase::OnReadingChangedCallback);
EventRegistrationToken event_token;
hr = sensor_->add_ReadingChanged(reading_changed_handler.Get(),
&event_token);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to add reading callback handler: "
<< logging::SystemErrorCodeToString(hr);
return false;
}
reading_callback_token_ = event_token;
}
return true;
}
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
void PlatformSensorReaderWinrtBase<
runtime_class_id,
ISensorWinrtStatics,
ISensorWinrtClass,
ISensorReadingChangedHandler,
ISensorReadingChangedEventArgs>::StopSensor() {
base::AutoLock autolock(lock_);
if (reading_callback_token_) {
HRESULT hr =
sensor_->remove_ReadingChanged(reading_callback_token_.value());
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to remove ALS reading callback handler: "
<< logging::SystemErrorCodeToString(hr);
}
reading_callback_token_ = base::nullopt;
}
}
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
PlatformSensorReaderWinrtBase<
runtime_class_id,
ISensorWinrtStatics,
ISensorWinrtClass,
ISensorReadingChangedHandler,
ISensorReadingChangedEventArgs>::~PlatformSensorReaderWinrtBase() {
StopSensor();
}
// static
std::unique_ptr<PlatformSensorReaderWinBase>
PlatformSensorReaderWinrtLightSensor::Create() {
auto light_sensor = std::make_unique<PlatformSensorReaderWinrtLightSensor>();
auto initialize_return = light_sensor->Initialize();
// Failing to query the default report interval is not a fatal error. The
// consumer (PlatformSensorWin) should be able to handle this and return a
// default report interval instead.
if ((initialize_return == SensorWinrtCreateFailure::kOk) ||
(initialize_return ==
SensorWinrtCreateFailure::kErrorGetMinReportIntervalFailed)) {
return light_sensor;
} else {
return nullptr;
}
}
HRESULT PlatformSensorReaderWinrtLightSensor::OnReadingChangedCallback(
ILightSensor* light_sensor,
ILightSensorReadingChangedEventArgs* reading_changed_args) {
ComPtr<ILightSensorReading> light_sensor_reading;
HRESULT hr = reading_changed_args->get_Reading(&light_sensor_reading);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to get the sensor reading: "
<< logging::SystemErrorCodeToString(hr);
// Failing to parse a reading sample should not be fatal so always
// return S_OK.
return S_OK;
}
float lux = 0.0f;
hr = light_sensor_reading->get_IlluminanceInLux(&lux);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to get the lux level: "
<< logging::SystemErrorCodeToString(hr);
return S_OK;
}
base::TimeDelta timestamp_delta;
hr = ConvertSensorReadingTimeStamp(light_sensor_reading, &timestamp_delta);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to get sensor reading timestamp: "
<< logging::SystemErrorCodeToString(hr);
return S_OK;
}
SensorReading reading{};
reading.als.value = lux;
reading.als.timestamp = timestamp_delta.InSecondsF();
client_->OnReadingUpdated(reading);
return S_OK;
} }
} // namespace device } // namespace device
\ No newline at end of file
...@@ -5,8 +5,16 @@ ...@@ -5,8 +5,16 @@
#ifndef SERVICES_DEVICE_GENERIC_SENSOR_PLATFORM_SENSOR_READER_WINRT_H_ #ifndef SERVICES_DEVICE_GENERIC_SENSOR_PLATFORM_SENSOR_READER_WINRT_H_
#define SERVICES_DEVICE_GENERIC_SENSOR_PLATFORM_SENSOR_READER_WINRT_H_ #define SERVICES_DEVICE_GENERIC_SENSOR_PLATFORM_SENSOR_READER_WINRT_H_
#include <windows.devices.sensors.h>
#include <windows.foundation.h>
#include <wrl/client.h>
#include <wrl/event.h>
#include <functional>
#include <memory> #include <memory>
#include "base/callback.h"
#include "base/optional.h"
#include "base/synchronization/lock.h"
#include "services/device/generic_sensor/platform_sensor_reader_win_base.h" #include "services/device/generic_sensor/platform_sensor_reader_win_base.h"
namespace device { namespace device {
...@@ -22,6 +30,117 @@ class PlatformSensorReaderWinrtFactory { ...@@ -22,6 +30,117 @@ class PlatformSensorReaderWinrtFactory {
mojom::SensorType type); mojom::SensorType type);
}; };
enum SensorWinrtCreateFailure {
kOk = 0,
kErrorISensorWinrtStaticsActivationFailed = 1,
kErrorGetDefaultSensorFailed = 2,
kErrorDefaultSensorNull = 3,
kErrorGetMinReportIntervalFailed = 4
};
// Base class that contains common helper functions used between all low
// level sensor types based on the Windows.Devices.Sensors API. Derived
// classes will specialize the template into a specific sensor. See
// PlatformSensorReaderWinrtLightSensor as an example of what WinRT
// interfaces should be passed in. The owner of this class must guarantee
// construction and destruction occur on the same thread and that no
// other thread is accessing it during destruction.
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
class PlatformSensorReaderWinrtBase : public PlatformSensorReaderWinBase {
public:
using GetSensorFactoryFunctor =
base::Callback<HRESULT(ISensorWinrtStatics**)>;
// Sets the client to notify changes about. The consumer should always
// ensure the lifetime of the client surpasses the lifetime of this class.
void SetClient(Client* client) override;
// Allows tests to specify their own implementation of the underlying sensor.
// This function should be called before Initialize().
void InitForTests(GetSensorFactoryFunctor get_sensor_factory_callback) {
get_sensor_factory_callback_ = get_sensor_factory_callback;
}
SensorWinrtCreateFailure Initialize() WARN_UNUSED_RESULT;
bool StartSensor(const PlatformSensorConfiguration& configuration) override
WARN_UNUSED_RESULT;
base::TimeDelta GetMinimalReportingInterval() const override;
void StopSensor() override;
protected:
PlatformSensorReaderWinrtBase();
virtual ~PlatformSensorReaderWinrtBase();
// Derived classes should implement this function to handle sensor specific
// parsing of the sensor reading.
virtual HRESULT OnReadingChangedCallback(
ISensorWinrtClass* sensor,
ISensorReadingChangedEventArgs* reading_changed_args) = 0;
// Helper function which converts the DateTime timestamp format the
// Windows.Devices.Sensors API uses to the second time ticks the
// client expects.
template <class ISensorReading>
HRESULT ConvertSensorReadingTimeStamp(
Microsoft::WRL::ComPtr<ISensorReading> sensor_reading,
base::TimeDelta* timestamp_delta);
// Following class member is protected by lock since SetClient,
// StartSensor, and StopSensor can all be called from different
// threads by PlatformSensorWin.
base::Lock lock_;
// Null if there is no client to notify, non-null otherwise.
Client* client_;
private:
base::TimeDelta GetMinimumReportIntervalFromSensor();
GetSensorFactoryFunctor get_sensor_factory_callback_;
// base::nullopt if the sensor has not been started, non-empty otherwise.
base::Optional<EventRegistrationToken> reading_callback_token_;
base::TimeDelta minimum_report_interval_;
Microsoft::WRL::ComPtr<ISensorWinrtClass> sensor_;
};
class PlatformSensorReaderWinrtLightSensor final
: public PlatformSensorReaderWinrtBase<
RuntimeClass_Windows_Devices_Sensors_LightSensor,
ABI::Windows::Devices::Sensors::ILightSensorStatics,
ABI::Windows::Devices::Sensors::ILightSensor,
Microsoft::WRL::Implements<
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
ABI::Windows::Foundation::ITypedEventHandler<
ABI::Windows::Devices::Sensors::LightSensor*,
ABI::Windows::Devices::Sensors::
LightSensorReadingChangedEventArgs*>,
Microsoft::WRL::FtmBase>,
ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs> {
public:
static std::unique_ptr<PlatformSensorReaderWinBase> Create();
PlatformSensorReaderWinrtLightSensor() = default;
~PlatformSensorReaderWinrtLightSensor() override = default;
protected:
HRESULT OnReadingChangedCallback(
ABI::Windows::Devices::Sensors::ILightSensor* sensor,
ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs*
reading_changed_args) override;
private:
PlatformSensorReaderWinrtLightSensor(
const PlatformSensorReaderWinrtLightSensor&) = delete;
PlatformSensorReaderWinrtLightSensor& operator=(
const PlatformSensorReaderWinrtLightSensor&) = delete;
};
} // namespace device } // namespace device
#endif // SERVICES_DEVICE_GENERIC_SENSOR_PLATFORM_SENSOR_READER_WINRT_H_ #endif // SERVICES_DEVICE_GENERIC_SENSOR_PLATFORM_SENSOR_READER_WINRT_H_
\ No newline at end of file
// Copyright 2019 The Chromium 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 "services/device/generic_sensor/platform_sensor_reader_winrt.h"
#include <objbase.h>
#include "base/test/bind_test_util.h"
#include "base/test/scoped_task_environment.h"
#include "base/win/core_winrt_util.h"
#include "base/win/scoped_com_initializer.h"
#include "services/device/generic_sensor/generic_sensor_consts.h"
#include "services/device/generic_sensor/platform_sensor_reader_win_base.h"
#include "services/device/public/cpp/generic_sensor/sensor_reading.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace device {
static constexpr unsigned long kExpectedMinimumReportInterval = 213;
static constexpr double kExpectedReportFrequencySet = 25.0;
static constexpr unsigned long kExpectedReportIntervalSet = 40;
// Base mock class for the Windows::Devices::Sensors::I*SensorReadings
template <class ISensorReading>
class FakeSensorReadingWinrt
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<
Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>,
ISensorReading> {
public:
FakeSensorReadingWinrt(ABI::Windows::Foundation::DateTime time_stamp)
: time_stamp_(time_stamp) {}
IFACEMETHOD(get_Timestamp)
(ABI::Windows::Foundation::DateTime* time_stamp) override {
*time_stamp = time_stamp_;
return get_timestamp_return_code_;
}
void SetGetTimestampReturnCode(HRESULT return_code) {
get_timestamp_return_code_ = return_code;
}
protected:
ABI::Windows::Foundation::DateTime time_stamp_;
HRESULT get_timestamp_return_code_ = S_OK;
};
class FakeLightSensorReadingWinrt
: public FakeSensorReadingWinrt<
ABI::Windows::Devices::Sensors::ILightSensorReading> {
public:
FakeLightSensorReadingWinrt(ABI::Windows::Foundation::DateTime time_stamp,
float lux)
: FakeSensorReadingWinrt(time_stamp), lux_(lux) {}
~FakeLightSensorReadingWinrt() override = default;
IFACEMETHOD(get_IlluminanceInLux)(float* lux) override {
*lux = lux_;
return get_illuminance_in_lux_return_code_;
}
void SetGetIlluminanceInLuxReturnCode(HRESULT return_code) {
get_illuminance_in_lux_return_code_ = return_code;
}
private:
float lux_;
HRESULT get_illuminance_in_lux_return_code_ = S_OK;
};
// Mock class for Windows::Devices::Sensors::ISensorReadingChangedEventArgs,
// allows reading changed events to return mock sensor readings.
template <class ISensorReading, class ISensorReadingChangedEventArgs>
class FakeSensorReadingChangedEventArgsWinrt
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<
Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>,
ISensorReadingChangedEventArgs> {
public:
FakeSensorReadingChangedEventArgsWinrt(
const Microsoft::WRL::ComPtr<ISensorReading>& reading)
: reading_(reading) {}
~FakeSensorReadingChangedEventArgsWinrt() override = default;
IFACEMETHOD(get_Reading)
(ISensorReading** reading) override { return reading_.CopyTo(reading); }
private:
Microsoft::WRL::ComPtr<ISensorReading> reading_;
};
// Mock client used to receive sensor data callbacks
class MockClient : public PlatformSensorReaderWinBase::Client {
public:
MOCK_METHOD1(OnReadingUpdated, void(const SensorReading& reading));
MOCK_METHOD0(OnSensorError, void());
protected:
~MockClient() override = default;
};
// Base mock class for Windows.Devices.Sensors.ISensor*, injected into
// PlatformSensorReaderWinrt* to mock out the underlying
// Windows.Devices.Sensors dependency.
template <class ISensor,
class Sensor,
class ISensorReading,
class ISensorReadingChangedEventArgs,
class SensorReadingChangedEventArgs>
class FakeSensorWinrt
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<
Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>,
ISensor> {
public:
~FakeSensorWinrt() override = default;
IFACEMETHOD(GetCurrentReading)
(ISensorReading** ppReading) override { return E_NOTIMPL; }
IFACEMETHOD(get_MinimumReportInterval)(UINT32* pValue) override {
*pValue = kExpectedMinimumReportInterval;
return get_minimum_report_interval_return_code_;
}
IFACEMETHOD(get_ReportInterval)(UINT32* pValue) override { return E_NOTIMPL; }
IFACEMETHOD(put_ReportInterval)(UINT32 value) override {
EXPECT_EQ(value, kExpectedReportIntervalSet);
return put_report_interval_return_code_;
}
IFACEMETHOD(add_ReadingChanged)
(ABI::Windows::Foundation::ITypedEventHandler<Sensor*,
SensorReadingChangedEventArgs*>*
pHandler,
EventRegistrationToken* pToken) override {
handler_ = pHandler;
return add_reading_changed_return_code_;
}
IFACEMETHOD(remove_ReadingChanged)(EventRegistrationToken iToken) override {
handler_.Reset();
return remove_reading_changed_return_code_;
}
// Makes any clients registered via add_ReadingChanged() to trigger with
// the given sensor reading.
void TriggerFakeSensorReading(
Microsoft::WRL::ComPtr<ISensorReading> reading) {
EXPECT_TRUE(handler_);
Microsoft::WRL::ComPtr<ISensorReadingChangedEventArgs> reading_event_args =
Microsoft::WRL::Make<FakeSensorReadingChangedEventArgsWinrt<
ISensorReading, ISensorReadingChangedEventArgs>>(reading);
EXPECT_HRESULT_SUCCEEDED(handler_->Invoke(this, reading_event_args.Get()));
}
// Returns true if any clients are registered for readings via
// add_ReadingChanged(), false otherwise.
bool IsSensorStarted() { return handler_; }
void SetGetMinimumReportIntervalReturnCode(HRESULT return_code) {
get_minimum_report_interval_return_code_ = return_code;
}
void SetPutReportIntervalReturnCode(HRESULT return_code) {
put_report_interval_return_code_ = return_code;
}
void SetAddReadingChangedReturnCode(HRESULT return_code) {
add_reading_changed_return_code_ = return_code;
}
void SetRemoveReadingChangedReturnCode(HRESULT return_code) {
remove_reading_changed_return_code_ = return_code;
}
private:
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::ITypedEventHandler<
Sensor*,
SensorReadingChangedEventArgs*>>
handler_;
HRESULT put_report_interval_return_code_ = S_OK;
HRESULT get_minimum_report_interval_return_code_ = S_OK;
HRESULT add_reading_changed_return_code_ = S_OK;
HRESULT remove_reading_changed_return_code_ = S_OK;
};
template <class ISensorStatics,
class ISensor,
class Sensor,
class ISensorReading,
class ISensorReadingChangedEventArgs,
class SensorReadingChangedEventArgs>
class FakeSensorFactoryWinrt
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<
Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>,
ISensorStatics> {
public:
FakeSensorFactoryWinrt() {
fake_sensor_ = Microsoft::WRL::Make<FakeSensorWinrt<
ABI::Windows::Devices::Sensors::ILightSensor,
ABI::Windows::Devices::Sensors::LightSensor,
ABI::Windows::Devices::Sensors::ILightSensorReading,
ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs,
ABI::Windows::Devices::Sensors::LightSensorReadingChangedEventArgs>>();
}
~FakeSensorFactoryWinrt() override = default;
IFACEMETHOD(GetDefault)(ISensor** ppResult) override {
if (fake_sensor_ && SUCCEEDED(get_default_return_code_)) {
return fake_sensor_.CopyTo(ppResult);
}
return get_default_return_code_;
}
Microsoft::WRL::ComPtr<FakeSensorWinrt<
ABI::Windows::Devices::Sensors::ILightSensor,
ABI::Windows::Devices::Sensors::LightSensor,
ABI::Windows::Devices::Sensors::ILightSensorReading,
ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs,
ABI::Windows::Devices::Sensors::LightSensorReadingChangedEventArgs>>
fake_sensor_;
void SetRemoveReadingChangedReturnCode(HRESULT return_code) {
get_default_return_code_ = return_code;
}
private:
HRESULT get_default_return_code_ = S_OK;
};
class PlatformSensorReaderTestWinrt : public testing::Test {
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
base::win::ScopedCOMInitializer scoped_com_initializer_;
};
// Tests that PlatformSensorReaderWinrtBase returns the expected error
// if it could not create the underlying sensor.
TEST_F(PlatformSensorReaderTestWinrt, FailedSensorCreate) {
auto fake_sensor_factory = Microsoft::WRL::Make<FakeSensorFactoryWinrt<
ABI::Windows::Devices::Sensors::ILightSensorStatics,
ABI::Windows::Devices::Sensors::ILightSensor,
ABI::Windows::Devices::Sensors::LightSensor,
ABI::Windows::Devices::Sensors::ILightSensorReading,
ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs,
ABI::Windows::Devices::Sensors::LightSensorReadingChangedEventArgs>>();
auto sensor = std::make_unique<PlatformSensorReaderWinrtLightSensor>();
fake_sensor_factory->fake_sensor_ = nullptr;
sensor->InitForTests(base::BindLambdaForTesting(
[&](ABI::Windows::Devices::Sensors::ILightSensorStatics** sensor_factory)
-> HRESULT { return fake_sensor_factory.CopyTo(sensor_factory); }));
EXPECT_EQ(sensor->Initialize(),
SensorWinrtCreateFailure::kErrorDefaultSensorNull);
fake_sensor_factory->SetRemoveReadingChangedReturnCode(E_FAIL);
EXPECT_EQ(sensor->Initialize(),
SensorWinrtCreateFailure::kErrorGetDefaultSensorFailed);
sensor->InitForTests(base::BindLambdaForTesting(
[&](ABI::Windows::Devices::Sensors::ILightSensorStatics** sensor_factory)
-> HRESULT { return E_FAIL; }));
EXPECT_EQ(
sensor->Initialize(),
SensorWinrtCreateFailure::kErrorISensorWinrtStaticsActivationFailed);
}
// Tests that PlatformSensorReaderWinrtBase returns the right
// minimum report interval.
TEST_F(PlatformSensorReaderTestWinrt, SensorMinimumReportInterval) {
auto fake_sensor_factory = Microsoft::WRL::Make<FakeSensorFactoryWinrt<
ABI::Windows::Devices::Sensors::ILightSensorStatics,
ABI::Windows::Devices::Sensors::ILightSensor,
ABI::Windows::Devices::Sensors::LightSensor,
ABI::Windows::Devices::Sensors::ILightSensorReading,
ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs,
ABI::Windows::Devices::Sensors::LightSensorReadingChangedEventArgs>>();
auto fake_sensor = fake_sensor_factory->fake_sensor_;
auto sensor = std::make_unique<PlatformSensorReaderWinrtLightSensor>();
sensor->InitForTests(base::BindLambdaForTesting(
[&](ABI::Windows::Devices::Sensors::ILightSensorStatics** sensor_factory)
-> HRESULT { return fake_sensor_factory.CopyTo(sensor_factory); }));
EXPECT_EQ(sensor->Initialize(), SensorWinrtCreateFailure::kOk);
EXPECT_EQ(sensor->GetMinimalReportingInterval().InMilliseconds(),
kExpectedMinimumReportInterval);
}
// Tests that PlatformSensorReaderWinrtBase returns a 0 report interval
// when it fails to query the sensor.
TEST_F(PlatformSensorReaderTestWinrt, FailedSensorMinimumReportInterval) {
auto fake_sensor_factory = Microsoft::WRL::Make<FakeSensorFactoryWinrt<
ABI::Windows::Devices::Sensors::ILightSensorStatics,
ABI::Windows::Devices::Sensors::ILightSensor,
ABI::Windows::Devices::Sensors::LightSensor,
ABI::Windows::Devices::Sensors::ILightSensorReading,
ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs,
ABI::Windows::Devices::Sensors::LightSensorReadingChangedEventArgs>>();
auto fake_sensor = fake_sensor_factory->fake_sensor_;
auto sensor = std::make_unique<PlatformSensorReaderWinrtLightSensor>();
fake_sensor->SetGetMinimumReportIntervalReturnCode(E_FAIL);
sensor->InitForTests(base::BindLambdaForTesting(
[&](ABI::Windows::Devices::Sensors::ILightSensorStatics** sensor_factory)
-> HRESULT { return fake_sensor_factory.CopyTo(sensor_factory); }));
EXPECT_EQ(sensor->Initialize(),
SensorWinrtCreateFailure::kErrorGetMinReportIntervalFailed);
EXPECT_EQ(sensor->GetMinimalReportingInterval().InMilliseconds(), 0);
}
// Tests that PlatformSensorReaderWinrtBase converts the timestamp correctly
TEST_F(PlatformSensorReaderTestWinrt, SensorTimestampConversion) {
static constexpr double expectedTimestampDeltaSecs = 19.0;
auto fake_sensor_factory = Microsoft::WRL::Make<FakeSensorFactoryWinrt<
ABI::Windows::Devices::Sensors::ILightSensorStatics,
ABI::Windows::Devices::Sensors::ILightSensor,
ABI::Windows::Devices::Sensors::LightSensor,
ABI::Windows::Devices::Sensors::ILightSensorReading,
ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs,
ABI::Windows::Devices::Sensors::LightSensorReadingChangedEventArgs>>();
auto fake_sensor = fake_sensor_factory->fake_sensor_;
auto sensor = std::make_unique<PlatformSensorReaderWinrtLightSensor>();
sensor->InitForTests(base::BindLambdaForTesting(
[&](ABI::Windows::Devices::Sensors::ILightSensorStatics** sensor_factory)
-> HRESULT { return fake_sensor_factory.CopyTo(sensor_factory); }));
EXPECT_EQ(sensor->Initialize(), SensorWinrtCreateFailure::kOk);
auto mock_client = std::make_unique<testing::NiceMock<MockClient>>();
double lastReportedTimestamp = 0.0;
EXPECT_CALL(*mock_client, OnReadingUpdated(::testing::_))
.WillRepeatedly(testing::Invoke([&](const SensorReading& reading) {
lastReportedTimestamp = reading.als.timestamp;
EXPECT_EQ(reading.als.value, 0.0f);
}));
sensor->SetClient(mock_client.get());
PlatformSensorConfiguration sensor_config(kExpectedReportFrequencySet);
EXPECT_TRUE(sensor->StartSensor(sensor_config));
// Trigger a sensor reading at time 0 (epoch), converted timestamp should
// also be 0.
Microsoft::WRL::ComPtr<ABI::Windows::Devices::Sensors::ILightSensorReading>
reading = Microsoft::WRL::Make<FakeLightSensorReadingWinrt>(
ABI::Windows::Foundation::DateTime{}, 0.0f);
fake_sensor->TriggerFakeSensorReading(reading);
EXPECT_EQ(lastReportedTimestamp, 0);
auto second_timestamp =
base::TimeDelta::FromSeconds(expectedTimestampDeltaSecs)
.ToWinrtDateTime();
reading =
Microsoft::WRL::Make<FakeLightSensorReadingWinrt>(second_timestamp, 0.0f);
fake_sensor->TriggerFakeSensorReading(reading);
// Verify the reported time stamp has ticked forward
// expectedTimestampDeltaSecs
EXPECT_EQ(lastReportedTimestamp, expectedTimestampDeltaSecs);
}
// Tests that PlatformSensorReaderWinrtBase starts and stops the
// underlying sensor when Start() and Stop() are called.
TEST_F(PlatformSensorReaderTestWinrt, StartStopSensorCallbacks) {
auto fake_sensor_factory = Microsoft::WRL::Make<FakeSensorFactoryWinrt<
ABI::Windows::Devices::Sensors::ILightSensorStatics,
ABI::Windows::Devices::Sensors::ILightSensor,
ABI::Windows::Devices::Sensors::LightSensor,
ABI::Windows::Devices::Sensors::ILightSensorReading,
ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs,
ABI::Windows::Devices::Sensors::LightSensorReadingChangedEventArgs>>();
auto fake_sensor = fake_sensor_factory->fake_sensor_;
auto sensor = std::make_unique<PlatformSensorReaderWinrtLightSensor>();
sensor->InitForTests(base::BindLambdaForTesting(
[&](ABI::Windows::Devices::Sensors::ILightSensorStatics** sensor_factory)
-> HRESULT { return fake_sensor_factory.CopyTo(sensor_factory); }));
EXPECT_EQ(sensor->Initialize(), SensorWinrtCreateFailure::kOk);
PlatformSensorConfiguration sensor_config(kExpectedReportFrequencySet);
EXPECT_TRUE(sensor->StartSensor(sensor_config));
EXPECT_TRUE(fake_sensor->IsSensorStarted());
// Calling Start() contiguously should not cause any errors/exceptions
EXPECT_TRUE(sensor->StartSensor(sensor_config));
EXPECT_TRUE(fake_sensor->IsSensorStarted());
sensor->StopSensor();
EXPECT_FALSE(fake_sensor->IsSensorStarted());
// Calling Stop() contiguously should not cause any errors/exceptions
sensor->StopSensor();
EXPECT_FALSE(fake_sensor->IsSensorStarted());
}
// Tests that PlatformSensorReaderWinrtBase stops the underlying sensor
// even if Start() is called without a following Stop() upon destruction.
TEST_F(PlatformSensorReaderTestWinrt, StartWithoutStopSensorCallbacks) {
auto fake_sensor_factory = Microsoft::WRL::Make<FakeSensorFactoryWinrt<
ABI::Windows::Devices::Sensors::ILightSensorStatics,
ABI::Windows::Devices::Sensors::ILightSensor,
ABI::Windows::Devices::Sensors::LightSensor,
ABI::Windows::Devices::Sensors::ILightSensorReading,
ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs,
ABI::Windows::Devices::Sensors::LightSensorReadingChangedEventArgs>>();
auto fake_sensor = fake_sensor_factory->fake_sensor_;
auto sensor = std::make_unique<PlatformSensorReaderWinrtLightSensor>();
sensor->InitForTests(base::BindLambdaForTesting(
[&](ABI::Windows::Devices::Sensors::ILightSensorStatics** sensor_factory)
-> HRESULT { return fake_sensor_factory.CopyTo(sensor_factory); }));
EXPECT_EQ(sensor->Initialize(), SensorWinrtCreateFailure::kOk);
PlatformSensorConfiguration sensor_config(kExpectedReportFrequencySet);
EXPECT_TRUE(sensor->StartSensor(sensor_config));
EXPECT_TRUE(fake_sensor->IsSensorStarted());
sensor.reset();
EXPECT_FALSE(fake_sensor->IsSensorStarted());
}
// Tests that PlatformSensorReaderWinrtBase::StartSensor() returns false
// when setting the report interval or registering for sensor events fails.
TEST_F(PlatformSensorReaderTestWinrt, FailedSensorStart) {
auto fake_sensor_factory = Microsoft::WRL::Make<FakeSensorFactoryWinrt<
ABI::Windows::Devices::Sensors::ILightSensorStatics,
ABI::Windows::Devices::Sensors::ILightSensor,
ABI::Windows::Devices::Sensors::LightSensor,
ABI::Windows::Devices::Sensors::ILightSensorReading,
ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs,
ABI::Windows::Devices::Sensors::LightSensorReadingChangedEventArgs>>();
auto fake_sensor = fake_sensor_factory->fake_sensor_;
auto sensor = std::make_unique<PlatformSensorReaderWinrtLightSensor>();
sensor->InitForTests(base::BindLambdaForTesting(
[&](ABI::Windows::Devices::Sensors::ILightSensorStatics** sensor_factory)
-> HRESULT { return fake_sensor_factory.CopyTo(sensor_factory); }));
EXPECT_EQ(sensor->Initialize(), SensorWinrtCreateFailure::kOk);
fake_sensor->SetPutReportIntervalReturnCode(E_FAIL);
PlatformSensorConfiguration sensor_config(kExpectedReportFrequencySet);
EXPECT_FALSE(sensor->StartSensor(sensor_config));
fake_sensor->SetPutReportIntervalReturnCode(S_OK);
fake_sensor->SetAddReadingChangedReturnCode(E_FAIL);
EXPECT_FALSE(sensor->StartSensor(sensor_config));
}
// Tests that PlatformSensorReaderWinrtBase::StopSensor() swallows
// unregister sensor change errors.
TEST_F(PlatformSensorReaderTestWinrt, FailedSensorStop) {
auto fake_sensor_factory = Microsoft::WRL::Make<FakeSensorFactoryWinrt<
ABI::Windows::Devices::Sensors::ILightSensorStatics,
ABI::Windows::Devices::Sensors::ILightSensor,
ABI::Windows::Devices::Sensors::LightSensor,
ABI::Windows::Devices::Sensors::ILightSensorReading,
ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs,
ABI::Windows::Devices::Sensors::LightSensorReadingChangedEventArgs>>();
auto fake_sensor = fake_sensor_factory->fake_sensor_;
auto sensor = std::make_unique<PlatformSensorReaderWinrtLightSensor>();
sensor->InitForTests(base::BindLambdaForTesting(
[&](ABI::Windows::Devices::Sensors::ILightSensorStatics** sensor_factory)
-> HRESULT { return fake_sensor_factory.CopyTo(sensor_factory); }));
EXPECT_EQ(sensor->Initialize(), SensorWinrtCreateFailure::kOk);
PlatformSensorConfiguration sensor_config(kExpectedReportFrequencySet);
EXPECT_TRUE(sensor->StartSensor(sensor_config));
fake_sensor->SetRemoveReadingChangedReturnCode(E_FAIL);
sensor->StopSensor();
}
// Tests that PlatformSensorReaderWinrtLightSensor does not notify the
// client if an error occurs during sensor sample parsing.
TEST_F(PlatformSensorReaderTestWinrt, FailedSensorSampleParse) {
auto fake_sensor_factory = Microsoft::WRL::Make<FakeSensorFactoryWinrt<
ABI::Windows::Devices::Sensors::ILightSensorStatics,
ABI::Windows::Devices::Sensors::ILightSensor,
ABI::Windows::Devices::Sensors::LightSensor,
ABI::Windows::Devices::Sensors::ILightSensorReading,
ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs,
ABI::Windows::Devices::Sensors::LightSensorReadingChangedEventArgs>>();
auto fake_sensor = fake_sensor_factory->fake_sensor_;
auto sensor = std::make_unique<PlatformSensorReaderWinrtLightSensor>();
sensor->InitForTests(base::BindLambdaForTesting(
[&](ABI::Windows::Devices::Sensors::ILightSensorStatics** sensor_factory)
-> HRESULT { return fake_sensor_factory.CopyTo(sensor_factory); }));
EXPECT_EQ(sensor->Initialize(), SensorWinrtCreateFailure::kOk);
auto mock_client = std::make_unique<testing::NiceMock<MockClient>>();
// This test relies on the NiceMock to work, if the sensor notifies
// the client despite not being able to parse the sensor sample then
// the NiceMock will throw an error as no EXPECT_CALL has been set.
sensor->SetClient(mock_client.get());
PlatformSensorConfiguration sensor_config(kExpectedReportFrequencySet);
EXPECT_TRUE(sensor->StartSensor(sensor_config));
auto reading = Microsoft::WRL::Make<FakeLightSensorReadingWinrt>(
ABI::Windows::Foundation::DateTime{}, 0.0f);
reading->SetGetTimestampReturnCode(E_FAIL);
fake_sensor->TriggerFakeSensorReading(reading);
reading->SetGetTimestampReturnCode(S_OK);
reading->SetGetIlluminanceInLuxReturnCode(E_FAIL);
fake_sensor->TriggerFakeSensorReading(reading);
}
// Tests that PlatformSensorReaderWinrtLightSensor notifies the client
// and gives it the correct sensor data when a new sensor sample arrives.
TEST_F(PlatformSensorReaderTestWinrt, SensorClientNotification) {
static constexpr float expected_lux = 123.0f;
auto fake_sensor_factory = Microsoft::WRL::Make<FakeSensorFactoryWinrt<
ABI::Windows::Devices::Sensors::ILightSensorStatics,
ABI::Windows::Devices::Sensors::ILightSensor,
ABI::Windows::Devices::Sensors::LightSensor,
ABI::Windows::Devices::Sensors::ILightSensorReading,
ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs,
ABI::Windows::Devices::Sensors::LightSensorReadingChangedEventArgs>>();
auto fake_sensor = fake_sensor_factory->fake_sensor_;
auto sensor = std::make_unique<PlatformSensorReaderWinrtLightSensor>();
sensor->InitForTests(base::BindLambdaForTesting(
[&](ABI::Windows::Devices::Sensors::ILightSensorStatics** sensor_factory)
-> HRESULT { return fake_sensor_factory.CopyTo(sensor_factory); }));
EXPECT_EQ(sensor->Initialize(), SensorWinrtCreateFailure::kOk);
auto mock_client = std::make_unique<testing::NiceMock<MockClient>>();
EXPECT_CALL(*mock_client, OnReadingUpdated(::testing::_))
.WillOnce(testing::Invoke([&](const SensorReading& reading) {
EXPECT_EQ(expected_lux, reading.als.value);
}));
sensor->SetClient(mock_client.get());
PlatformSensorConfiguration sensor_config(kExpectedReportFrequencySet);
EXPECT_TRUE(sensor->StartSensor(sensor_config));
auto reading = Microsoft::WRL::Make<FakeLightSensorReadingWinrt>(
ABI::Windows::Foundation::DateTime{}, expected_lux);
fake_sensor->TriggerFakeSensorReading(reading);
sensor->StopSensor();
}
} // namespace device
\ No newline at end of file
...@@ -4,8 +4,10 @@ ...@@ -4,8 +4,10 @@
Windows has two sets of sensor APIs: Windows has two sets of sensor APIs:
- [ISensor API](https://docs.microsoft.com/en-us/windows/desktop/sensorsapi/about-the-sensor-api): The COM sensor API on Windows, which is now deprecated. - [ISensor API](https://docs.microsoft.com/en-us/windows/desktop/sensorsapi/about-the-sensor-api):
- [Windows.Devices.Sensors WinRT API](https://docs.microsoft.com/en-us/uwp/api/windows.devices.sensors): The WinRT sensor API on Windows. The Win32 sensor API on Windows, which is now deprecated.
- [Windows.Devices.Sensors WinRT API](https://docs.microsoft.com/en-us/uwp/api/windows.devices.sensors):
The WinRT sensor API on Windows.
This document outlines the current ISensor based Chromium sensor backend on This document outlines the current ISensor based Chromium sensor backend on
Windows and the newly proposed Windows.Devices.Sensors based Windows and the newly proposed Windows.Devices.Sensors based
...@@ -13,18 +15,16 @@ implementation. It is laid out as follows: ...@@ -13,18 +15,16 @@ implementation. It is laid out as follows:
1. Summary 1. Summary
2. General Chromium Sensor Backend Model 2. General Chromium Sensor Backend Model
3. Current ISensor Chromium Sensor Backend Model on Windows 3. ISensor Chromium Sensor Backend Model on Windows
4. Proposed Windows.Devices.Sensors Chromium Sensor Backend Model on Windows 4. Windows.Devices.Sensors Chromium Sensor Backend Model on Windows
5. Chromium Feature Flag 5. Chromium Feature Flag
6. Metrics 6. Metrics
7. Incremental Change Plan 7. Incremental Change Plan
8. Appendix
Note: This document has sections that read like a future looking document, Note: This document has sections that read like a future looking document,
(e.g. "will do this..." and "proposed design...") as the (e.g. "will do this..." and "proposed design...") as those
Windows.Devices.Sensors parts have not been implemented. Once they are Windows.Devices.Sensors sections have not been implemented. Once they are
implemented, those sections will be updated to present tense and have the implemented, those sections will be updated to present tense or removed.
corresponding sample code removed.
## 2. General Chromium Sensor Backend Model ## 2. General Chromium Sensor Backend Model
...@@ -42,7 +42,7 @@ These interfaces are ultimately what the ...@@ -42,7 +42,7 @@ These interfaces are ultimately what the
[W3C sensor interface](https://www.w3.org/TR/generic-sensor/) [W3C sensor interface](https://www.w3.org/TR/generic-sensor/)
communicates with - each platform has its own implementation. communicates with - each platform has its own implementation.
## 3. Current ISensor Chromium Sensor Backend Model on Windows ## 3. ISensor Chromium Sensor Backend Model on Windows
The following diagram shows the ISensor based Chromium backend The following diagram shows the ISensor based Chromium backend
implementation on Windows: implementation on Windows:
...@@ -89,11 +89,11 @@ The "Sensor GUID" column specifies the names of the sensor type GUIDs ...@@ -89,11 +89,11 @@ The "Sensor GUID" column specifies the names of the sensor type GUIDs
used to provide data for a SensorType. Any SensorType not mentioned by used to provide data for a SensorType. Any SensorType not mentioned by
this table are not supported on Windows. this table are not supported on Windows.
## 4. Proposed Windows.Devices.Sensors Chromium Sensor Backend Model on Windows ## 4. Windows.Devices.Sensors Chromium Sensor Backend Model on Windows
The overall sensor backend design on Windows will remain the same The overall sensor backend design on Windows remains the same
(as detailed in section 3). The classes which take a direct dependency (as detailed in section 3). The classes which take a direct dependency
on the ISensor API will have their interfaces abstracted out and on the ISensor API have their interfaces abstracted out and
reimplemented with Windows.Devices.Sensors: reimplemented with Windows.Devices.Sensors:
- The PlatformSensorProviderWin class: - The PlatformSensorProviderWin class:
...@@ -117,21 +117,15 @@ will switch to use the ISensor implementation on versions of Windows that ...@@ -117,21 +117,15 @@ will switch to use the ISensor implementation on versions of Windows that
do not have WinRT thresholding support and will use the Windows.Devices.Sensors do not have WinRT thresholding support and will use the Windows.Devices.Sensors
implementation for those that do. implementation for those that do.
This section is split into two main parts:
- Modifying the Chromium sensor backend on Windows to support adapting
between the ISensor and Windows.Devices.Sensors path.
- Outlining the new Windows.Devices.Sensors implementation.
### 4.1 Support For Adapting Between ISensor and Windows.Devices.Sensors Sensor Implementations ### 4.1 Support For Adapting Between ISensor and Windows.Devices.Sensors Sensor Implementations
Please refer to [platform_sensor_provider.cc](platform_sensor_provider.cc ). Please refer to [platform_sensor_provider.cc](../platform_sensor_provider.cc).
### 4.2 Proposed Windows.Devices.Sensors Sensor Implementation ### 4.2 Proposed Windows.Devices.Sensors Sensor Implementation
The overall Windows.Devices.Sensors design is the same as the current design The overall Windows.Devices.Sensors design is the same as the current design
detailed in section 3 except the classes that take a dependency on the ISensor detailed in section 3 except the classes that take a dependency on the ISensor
API will be reimplemented with these new classes: API have been reimplemented with these new classes:
- PlatformSensorProviderWinrt - PlatformSensorProviderWinrt
- Windows.Devices.Sensors based implementation of PlatformSensorProvider, - Windows.Devices.Sensors based implementation of PlatformSensorProvider,
...@@ -143,161 +137,18 @@ API will be reimplemented with these new classes: ...@@ -143,161 +137,18 @@ API will be reimplemented with these new classes:
#### 4.2.1 PlatformSensorReaderWinrt #### 4.2.1 PlatformSensorReaderWinrt
The existing PlatformSensorReaderWin class will be pulled out into The existing PlatformSensorReaderWin class will be pulled out into
its own interface: its own interface in [platform_sensor_reader_win_base.h](../platform_sensor_reader_win_base.h).
```cpp The existing (ISensor) PlatformSensorReaderWin32 class and new
class PlatformSensorReaderWinBase { (Windows.Devices.Sensors) PlatformSensorReaderWinrt class inherit from
public:
static std::unique_ptr<PlatformSensorReaderWin> Create(
mojom::SensorType type);
// Following methods are thread safe.
virtual void SetClient(Client* client) = 0;
virtual unsigned long GetMinimalReportingIntervalMs() const = 0;
virtual bool StartSensor(const PlatformSensorConfiguration& configuration) = 0;
virtual void StopSensor() = 0;
// Must be destructed on the same thread that was used during construction.
virtual ~PlatformSensorReaderWin() = default;
}
```
The existing (ISensor) PlatformSensorReaderWin class and new
(Windows.Devices.Sensors) PlatformSensorReaderWinrt class will inherit from
this interface so both classes can be interchangeably used with the this interface so both classes can be interchangeably used with the
ISensor/Windows.Devices.Sensors agnostic PlatformSensorWin, which simply ISensor/Windows.Devices.Sensors agnostic PlatformSensorWin, which simply
consumes the PlatformSensorReaderWinBase interface. consumes the PlatformSensorReaderWinBase interface.
Since there are several Windows.Devices.Sensors sensor functions which are Since there are several Windows.Devices.Sensors sensor functions which are
identical between the different sensor types, an abstract class will identical between the different sensor types, an abstract class
encapsulate the common functionality. encapsulates the common functionality. Please refer to
[platform_sensor_reader_winrt.h](../platform_sensor_reader_winrt.h).
`platform_sensor_reader_winrt.h`:
```cpp
// Base class for PlatformSensorReaderWinrt*, contains common helper
// functions used between all WinRT sensors. Derived classes will
// specialize into a specific sensor and are expected to pass in
// the correct WinRT interfaces into the template.
//
// See PlatformSensorReaderWinrtLightSensor as an example for
// what WinRT interfaces should be passed in.
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
class PlatformSensorReaderWinrtBase : public PlatformSensorReaderWin {
public:
bool Initialize();
// Sets the client to notify changes about.
void SetClient(Client* client) override;
// Allows tests to specify their own implementation of the underlying sensor.
void InitForTests(Microsoft::WRL::ComPtr<ISensorWinrtClass> sensor) {
sensor_ = sensor;
sensor_->get_MinimumReportInterval(&minimum_report_interval_);
}
bool StartSensor(const PlatformSensorConfiguration& configuration) override;
unsigned long GetMinimalReportingIntervalMs() const override;
void StopSensor() override;
protected:
virtual HRESULT OnReadingChangedCallback(
ISensorWinrtClass* sensor,
ISensorReadingChangedEventArgs* reading_changed_args) = 0;
// Helper function which converts the DateTime format the WinRT API
// uses to second time ticks the client expects.
HRESULT ConvertSensorReadingTimeStamp(
ABI::Windows::Foundation::DateTime date_time_to_convert,
double* time_ticks_seconds);
// Following class members are protected by lock, because SetClient,
// StartSensor and StopSensor are called from another thread by
// PlatformSensorWin that can modify internal state of the object.
base::Lock lock_;
// Null if there is no client to notify, non-null otherwise.
Client* client_;
// base::nullopt if the sensor has not been started, non-empty otherwise.
base::Optional<EventRegistrationToken> reading_callback_token_;
UINT32 minimum_report_interval_{};
Microsoft::WRL::ComPtr<ISensorWinrtClass> sensor_;
};
```
Each sensor type will derive from the PlatformSensorReaderWinrtBase
abstract class. Below is an example for the light sensor:
`platform_sensor_reader_winrt.h`:
```cpp
class PlatformSensorReaderWinrtLightSensor final
: public PlatformSensorReaderWinrtBase<
RuntimeClass_Windows_Devices_Sensors_LightSensor,
ABI::Windows::Devices::Sensors::ILightSensorStatics,
ABI::Windows::Devices::Sensors::ILightSensor,
Microsoft::WRL::Implements<
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
ABI::Windows::Foundation::ITypedEventHandler<
ABI::Windows::Devices::Sensors::LightSensor*,
ABI::Windows::Devices::Sensors::
LightSensorReadingChangedEventArgs*>,
Microsoft::WRL::FtmBase>,
ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs> {
public:
static std::unique_ptr<PlatformSensorReaderWin> Create();
PlatformSensorReaderWinrtLightSensor();
protected:
HRESULT OnReadingChangedCallback(
ABI::Windows::Devices::Sensors::ILightSensor* sensor,
ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs*
reading_changed_args) override;
private:
float last_reported_lux_{};
DISALLOW_COPY_AND_ASSIGN(PlatformSensorReaderWinrtLightSensor);
};
```
The implementation for PlatformSensorReaderWinrtBase and
PlatformSensorReaderWinrtLightSensor have been deferred to section 8.1
and 8.2 of the appendix for brevity.
Lastly, PlatformSensorReaderWinrt::Create will choose which derived
PlatformSensorReaderWin class to instantiate based on the requested
sensor type:
```cpp
std::unique_ptr<PlatformSensorReaderWin> PlatformSensorReaderWinrt::Create(
mojom::SensorType type) {
case mojom::SensorType::AMBIENT_LIGHT:
return PlatformSensorReaderWinrtLightSensor::Create();
case mojom::SensorType::ACCELEROMETER:
return PlatformSensorReaderWinrtAccelerometer::Create();
...
default:
NOTIMPLEMENTED();
return nullptr;
}
}
```
Required mocks for unit testing:
- Implement the mock Windows.Devices.Sensors interface classes.
- At the start of the unit test, inject the mock Windows.Devices.Sensors class
to use via `InitForTests()`.
Unit test cases for this class are detailed in section 7.
## 5. Chromium Feature Flag ## 5. Chromium Feature Flag
...@@ -356,49 +207,17 @@ thresholding. ...@@ -356,49 +207,17 @@ thresholding.
The modernization changes will be broken down into several incremental The modernization changes will be broken down into several incremental
changes to keep change lists to a reviewable size: changes to keep change lists to a reviewable size:
#### Change list 3: Define the interface for PlatformSensorReaderWinBase and implement PlatformSensorReaderWinrtBase ### Change list 5-6: Implement the rest of the sensors
- Feature Work:
- Extract the PlatformSensorReaderWin interface into its own file as
defined in section 4.2.2.
- Create and implement PlatformSensorReaderWinrtBase as defined in
section 8.2.
- Testing:
- Add unit tests for PlatformSensorReaderWinrtBase:
- Validate `ConvertSensorReadingTimeStamp()` correctly converts
Windows.Devices.Sensors DateTime to base::TimeTick.
- Validate `GetMinimalReportingIntervalMs()` returns the correct
minimum report interval.
- Validate `StartSensor()` and `StopSensor()` registers and
unregisters for Windows.Devices.Sensors reading change notifications.
- Validate a client notification occurs when a new sample is
reported.
#### Change list 4: Implement PlatformSensorReaderWinrtLightSensor
- Feature Work:
- Create and implement PlatformSensorReaderWinrtLightSensor as
defined in section 8.3.
- Testing
- Add unit tests for PlatformSensorReaderWinrtLightSensor:
- Create a mock ILightSensor class.
- Validate PlatformSensorReaderWinrtLightSensor correctly
parses new Windows.Devices.Sensors samples and the client
receives the expected lux level.
#### Change list 5-8: Implement the rest of the sensors
- Feature Work: - Feature Work:
- Same as CL 4 but implementing the accelerometer, gyroscope, - Implement the accelerometer, gyroscope, magnetometer, and absolute
magnetometer, and absolute orientation classes. orientation classes similar to the light sensor.
- Testing - Testing
- Same as CL 4 but implementing unit tests for the accelerometer, - Implementing unit tests for the accelerometer, gyroscope,
gyroscope, magnetometer, and absolute orientation classes. magnetometer, and absolute orientation classes similar to the light
sensor.
Each sensor class will have its own corresponding CL, so this #### Change list 7: Add metrics
section actually represents four separate CLs.
#### Change list 9: Add metrics
- Feature Work: - Feature Work:
- Add metrics as defined in section 6 following these - Add metrics as defined in section 6 following these
...@@ -408,7 +227,7 @@ section actually represents four separate CLs. ...@@ -408,7 +227,7 @@ section actually represents four separate CLs.
as an example. as an example.
- Run histogram tests as defined [here](https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md#Testing). - Run histogram tests as defined [here](https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md#Testing).
#### Change list 10: Roll out new Windows.Devices.Sensors implementation to users #### Change list 8: Roll out new Windows.Devices.Sensors implementation to users
- Feature Work: - Feature Work:
- Use experimentation process to roll out the new implementation - Use experimentation process to roll out the new implementation
...@@ -417,292 +236,8 @@ section actually represents four separate CLs. ...@@ -417,292 +236,8 @@ section actually represents four separate CLs.
- Build Chromium and manually verify the reported samples are the - Build Chromium and manually verify the reported samples are the
expected values. expected values.
#### Change list 11: Enable Windows.Devices.Sensors implementation by default #### Change list 9: Enable Windows.Devices.Sensors implementation by default
- Feature Work: - Feature Work:
- Change the feature flag to enabled by default so it is enabled for - Change the feature flag to enabled by default so it is enabled for
all users. all users.
## 8. Appendix
### 8.1 PlatformSensorReaderWinrtBase Implementation
`platform_sensor_reader_winrt.h`:
Located in section 4.2.2 above.
`platform_sensor_reader_winrt.cpp`:
```cpp
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
void PlatformSensorReaderWinrtBase<
runtime_class_id,
ISensorWinrtStatics,
ISensorWinrtClass,
ISensorReadingChangedHandler,
ISensorReadingChangedEventArgs>::SetClient(Client* client) {
base::AutoLock autolock(lock_);
client_ = client;
}
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
HRESULT PlatformSensorReaderWinrtBase<runtime_class_id,
ISensorWinrtStatics,
ISensorWinrtClass,
ISensorReadingChangedHandler,
ISensorReadingChangedEventArgs>::
ConvertSensorReadingTimeStamp(
ABI::Windows::Foundation::DateTime date_time_to_convert,
double* time_ticks_seconds) {
// Convert the DateTime into SYSTEMTIME through a series of
// intermediary conversions.
ULARGE_INTEGER date_time_converted{};
date_time_converted.QuadPart = date_time_to_convert.UniversalTime;
FILETIME file_time{};
file_time.dwHighDateTime = date_time_converted.HighPart;
file_time.dwLowDateTime = date_time_converted.LowPart;
SYSTEMTIME system_time{};
if (!FileTimeToSystemTime(&file_time, &system_time)) {
return HRESULT_FROM_WIN32(GetLastError());
}
// Convert the SYSTEMTIME into base::Time
base::Time::Exploded exploded{};
exploded.year = system_time.wYear;
exploded.month = system_time.wMonth;
exploded.day_of_week = system_time.wDayOfWeek;
exploded.day_of_month = system_time.wDay;
exploded.hour = system_time.wHour;
exploded.minute = system_time.wMinute;
exploded.second = system_time.wSecond;
exploded.millisecond = system_time.wMilliseconds;
base::Time converted_time{};
if (!base::Time::FromUTCExploded(exploded, &converted_time)) {
return E_FAIL;
}
// Lastly, convert the base::Time into base::TimeTicks()
base::TimeTicks ticks_now = base::TimeTicks::Now();
base::Time time_now = base::Time::NowFromSystemTime();
base::TimeDelta delta = time_now - converted_time;
*time_ticks_seconds = ((ticks_now - delta) - base::TimeTicks()).InSecondsF();
return S_OK;
}
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
bool PlatformSensorReaderWinrtBase<
runtime_class_id,
ISensorWinrtStatics,
ISensorWinrtClass,
ISensorReadingChangedHandler,
ISensorReadingChangedEventArgs>::Initialize() {
ComPtr<ISensorWinrtStatics> sensor_statics;
HRESULT hr =
base::win::GetActivationFactory<ISensorWinrtStatics, runtime_class_id>(
&sensor_statics);
if (FAILED(hr))
return false;
if (FAILED(sensor_statics->GetDefault(&sensor_)))
return false;
// GetDefault() returns null if the sensor does not exist
if (!sensor_)
return false;
// OK if this fails, sensor will fallback to 0 which the consumer
// handles gracefully
hr = sensor_->get_MinimumReportInterval(&minimum_report_interval_);
if (FAILED(hr)) {
LOG(WARNING) << "Failed to query sensor minimum report interval: "
<< logging::SystemErrorCodeToString(hr);
}
return true;
}
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
unsigned long PlatformSensorReaderWinrtBase<
runtime_class_id,
ISensorWinrtStatics,
ISensorWinrtClass,
ISensorReadingChangedHandler,
ISensorReadingChangedEventArgs>::GetMinimalReportingIntervalMs() const {
return minimum_report_interval_;
}
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
bool PlatformSensorReaderWinrtBase<runtime_class_id,
ISensorWinrtStatics,
ISensorWinrtClass,
ISensorReadingChangedHandler,
ISensorReadingChangedEventArgs>::
StartSensor(const PlatformSensorConfiguration& configuration) {
base::AutoLock autolock(lock_);
if (!reading_callback_token_) {
EventRegistrationToken event_token;
unsigned int interval =
(1 / configuration.frequency()) * base::Time::kMillisecondsPerSecond;
auto hr = sensor_->put_ReportInterval(interval);
if (FAILED(hr)) {
LOG(ERROR) << "Failed to set report interval: "
<< logging::SystemErrorCodeToString(hr);
return false;
}
auto reading_changed_handler = Callback<ISensorReadingChangedHandler>(
this, &PlatformSensorReaderWinrtBase::OnReadingChangedCallback);
hr = sensor_->add_ReadingChanged(reading_changed_handler.Get(),
&event_token);
if (FAILED(hr)) {
LOG(ERROR) << "Failed to add reading callback handler: "
<< logging::SystemErrorCodeToString(hr);
return false;
}
reading_callback_token_ =
base::make_optional<EventRegistrationToken>(event_token);
}
return true;
}
template <wchar_t const* runtime_class_id,
class ISensorWinrtStatics,
class ISensorWinrtClass,
class ISensorReadingChangedHandler,
class ISensorReadingChangedEventArgs>
void PlatformSensorReaderWinrtBase<
runtime_class_id,
ISensorWinrtStatics,
ISensorWinrtClass,
ISensorReadingChangedHandler,
ISensorReadingChangedEventArgs>::StopSensor() {
base::AutoLock autolock(lock_);
if (reading_callback_token_) {
HRESULT hr =
sensor_->remove_ReadingChanged(reading_callback_token_.value());
if (FAILED(hr)) {
LOG(ERROR) << "Failed to remove ALS reading callback handler: "
<< logging::SystemErrorCodeToString(hr);
}
reading_callback_token_ = base::nullopt;
}
}
```
### 8.2 PlatformSensorReaderWinrtLightSensor Implementation
`platform_sensor_reader_winrt.h`:
Located in section 4.2.2 above.
`platform_sensor_reader_winrt.cpp`:
```cpp
// static
std::unique_ptr<PlatformSensorReaderWin>
PlatformSensorReaderWinrtLightSensor::Create() {
auto light_sensor = std::make_unique<PlatformSensorReaderWinrtLightSensor>();
if (!light_sensor->Initialize())
return nullptr;
return light_sensor;
}
PlatformSensorReaderWinrtLightSensor::PlatformSensorReaderWinrtLightSensor() { }
HRESULT PlatformSensorReaderWinrtLightSensor::OnReadingChangedCallback(
ILightSensor* light_sensor,
ILightSensorReadingChangedEventArgs* reading_changed_args) {
SensorReading reading;
ComPtr<ILightSensorReading> light_sensor_reading;
HRESULT hr = reading_changed_args->get_Reading(&light_sensor_reading);
if (FAILED(hr)) {
LOG(ERROR) << "Failed to get the sensor reading: "
<< logging::SystemErrorCodeToString(hr);
return hr;
}
float lux{};
hr = light_sensor_reading->get_IlluminanceInLux(&lux);
if (FAILED(hr)) {
LOG(ERROR) << "Failed to get the lux level: "
<< logging::SystemErrorCodeToString(hr);
return hr;
}
DateTime timestamp{};
hr = light_sensor_reading->get_Timestamp(&timestamp);
if (FAILED(hr)) {
LOG(ERROR) << "Failed to get the sensor reading timestamp: "
<< logging::SystemErrorCodeToString(hr);
return hr;
}
double time_ticks_seconds{};
hr = ConvertSensorReadingTimeStamp(timestamp, &time_ticks_seconds);
if (FAILED(hr)) {
LOG(ERROR) << "Failed to convert sensor reading timestamp format: "
<< logging::SystemErrorCodeToString(hr);
return hr;
}
reading.als.value = lux;
reading.als.timestamp = time_ticks_seconds;
// The WinRT API currently only supports streaming so it is possible
// to get sequential sensor readings with identical lux values. In
// this case, the client should not be notified of a reading change.
if ((lux != last_reported_lux_) && client_) {
client_->OnReadingUpdated(reading);
last_reported_lux_ = lux;
}
return S_OK;
}
```
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment