blob: 9e50d6ba73c032381076661b1b0cde0e777cb294 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/smart_card/smart_card_reader_tracker_impl.h"
#include "base/functional/callback.h"
namespace content {
namespace {
// Special string that, when specified as the reader name in GetStatusChange(),
// enables the client to be notified when a new smart card reader is added to
// the system.
// This a hack put on top of the PC/SC spec (which predates USB) and supported
// by most winscard implementations.
static const char kPnpNotification[] = R"(\\?PnP?\Notification)";
constexpr auto kStatusChangeTimeout = base::Minutes(5);
// Converts the mess that is the SCARD_STATE_* flags into a value of our flat
// enumeration.
//
// Shouldn't be called with `unaware`, `ignore` or `unknown` flags set as they
// have special meaning and should be handled elsewhere.
// Ie, they do not translate to a blink::mojom::SmartCardReaderState value.
blink::mojom::SmartCardReaderState ToBlinkSmartCardReaderState(
const device::mojom::SmartCardReaderStateFlags& flags) {
CHECK(!flags.unaware);
CHECK(!flags.ignore);
CHECK(!flags.unknown);
if (flags.unavailable) {
return blink::mojom::SmartCardReaderState::kUnavailable;
}
if (flags.empty) {
return blink::mojom::SmartCardReaderState::kEmpty;
}
if (flags.exclusive) {
if (!flags.present) {
// The browser has no control of the platforms' PC/SC stack. Thus even
// though this combination shouldn't happen, technically nothing stops the
// PC/SC stack from returning such a combination of flags.
// Same applies for other warnings in this function.
LOG(WARNING) << "It's invalid to have SCARD_STATE_EXCLUSIVE without "
"SCARD_STATE_PRESENT.";
}
return blink::mojom::SmartCardReaderState::kExclusive;
}
if (flags.inuse) {
if (!flags.present) {
LOG(WARNING) << "It's invalid to have SCARD_STATE_INUSE without "
"SCARD_STATE_PRESENT.";
}
return blink::mojom::SmartCardReaderState::kInuse;
}
if (flags.mute) {
if (!flags.present) {
LOG(WARNING) << "It's invalid to have SCARD_STATE_MUTE without "
"SCARD_STATE_PRESENT.";
}
return blink::mojom::SmartCardReaderState::kMute;
}
if (flags.unpowered) {
if (!flags.present) {
LOG(WARNING) << "It's invalid to have SCARD_STATE_UNPOWERED without "
"SCARD_STATE_PRESENT.";
}
return blink::mojom::SmartCardReaderState::kUnpowered;
}
if (flags.present) {
// There is a card and it's powered, responsive and not in use
// by any other application.
return blink::mojom::SmartCardReaderState::kPresent;
}
LOG(ERROR) << "Invalid ReaderStateFlags";
return blink::mojom::SmartCardReaderState::kUnavailable;
}
void FailRequests(base::queue<SmartCardReaderTracker::StartCallback>& requests,
device::mojom::SmartCardError error) {
while (!requests.empty()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(
std::move(requests.front()),
blink::mojom::SmartCardGetReadersResult::NewError(error)));
requests.pop();
}
}
} // namespace
// Contains the known state of a smart card reader.
class SmartCardReaderTrackerImpl::Reader {
public:
explicit Reader(const device::mojom::SmartCardReaderStateOut& state_out)
: blink_reader_info_(blink::mojom::SmartCardReaderInfo::New(
state_out.reader,
ToBlinkSmartCardReaderState(*state_out.event_state.get()),
state_out.answer_to_reset)) {
CopyStateFlags(*state_out.event_state.get());
}
// Returns whether any change was made.
bool Update(const device::mojom::SmartCardReaderStateOut& state_out) {
CHECK_EQ(state_out.reader, blink_reader_info_->name);
bool changed = false;
if (blink_reader_info_->atr != state_out.answer_to_reset) {
blink_reader_info_->atr = state_out.answer_to_reset;
changed = true;
}
blink::mojom::SmartCardReaderState new_blink_state =
ToBlinkSmartCardReaderState(*state_out.event_state.get());
if (new_blink_state != blink_reader_info_->state) {
blink_reader_info_->state = new_blink_state;
changed = true;
}
CopyStateFlags(*state_out.event_state.get());
return changed;
}
device::mojom::SmartCardReaderStateFlagsPtr CreateCurrentStateFlags() {
auto flags = device::mojom::SmartCardReaderStateFlags::New();
flags->unavailable = unavailable_;
flags->empty = empty_;
flags->present = present_;
flags->exclusive = exclusive_;
flags->inuse = inuse_;
flags->mute = mute_;
flags->unpowered = unpowered_;
return flags;
}
const blink::mojom::SmartCardReaderInfo& blink_reader_info() const {
return *blink_reader_info_.get();
}
private:
void CopyStateFlags(
const device::mojom::SmartCardReaderStateFlags& state_flags) {
unavailable_ = state_flags.unavailable;
empty_ = state_flags.empty;
present_ = state_flags.present;
exclusive_ = state_flags.exclusive;
inuse_ = state_flags.inuse;
mute_ = state_flags.mute;
unpowered_ = state_flags.unpowered;
}
blink::mojom::SmartCardReaderInfoPtr blink_reader_info_;
// As received from the last GetStatusChange() run.
// To be used on the next GetStatusChange() call as the known/expected state.
//
// Note that there's no strict 1:1 mapping between those and
// blink::mojom::SmartCardReaderState. Therefore we have to store them
// separately.
bool unavailable_;
bool empty_;
bool present_;
bool exclusive_;
bool inuse_;
bool mute_;
bool unpowered_;
};
////////////////////////////////////////////////////////////////////////////////
// SmartCardReaderTrackerImpl::State
// Represents a state in the `SmartCardReaderTracker` state machine.
// It defines how the `SmartCardReaderTracker` reacts to calls to its public
// methods and to its callbacks.
class SmartCardReaderTrackerImpl::State {
public:
explicit State(SmartCardReaderTrackerImpl& tracker) : tracker_(tracker) {}
virtual ~State() = default;
virtual std::string ToString() const = 0;
virtual void Enter() {}
virtual void Start(StartCallback) {}
protected:
// Owns `this`.
const raw_ref<SmartCardReaderTrackerImpl> tracker_;
};
////////////////////////////////////////////////////////////////////////////////
// SmartCardReaderTrackerImpl::Uninitialized declaration
// Initial state of `SmartCardReaderTracker`.
class SmartCardReaderTrackerImpl::Uninitialized
: public SmartCardReaderTrackerImpl::State {
public:
explicit Uninitialized(SmartCardReaderTrackerImpl& tracker)
: State(tracker) {}
std::string ToString() const override;
void Start(StartCallback) override;
};
////////////////////////////////////////////////////////////////////////////////
// SmartCardReaderTrackerImpl::WaitReadersList declaration
// `SmartCardReaderTracker` called `ListReaders` and is waiting for its result.
class SmartCardReaderTrackerImpl::WaitReadersList
: public SmartCardReaderTrackerImpl::State {
public:
WaitReadersList(SmartCardReaderTrackerImpl& tracker,
mojo::Remote<device::mojom::SmartCardContext> context,
base::queue<StartCallback> pending_get_readers_requests =
base::queue<StartCallback>());
WaitReadersList(
SmartCardReaderTrackerImpl& tracker,
mojo::PendingRemote<device::mojom::SmartCardContext> pending_context,
base::queue<StartCallback> pending_get_readers_requests);
~WaitReadersList() override;
std::string ToString() const override;
void Enter() override;
void Start(StartCallback callback) override;
private:
void OnListReadersDone(device::mojom::SmartCardListReadersResultPtr result);
void RemoveAbsentReaders(const std::vector<std::string>& current_readers);
std::vector<std::string> IdentifyNewReaders(
const std::vector<std::string>& current_readers);
void ReplyNoReaders();
mojo::Remote<device::mojom::SmartCardContext> context_;
base::queue<StartCallback> pending_get_readers_requests_;
base::WeakPtrFactory<SmartCardReaderTrackerImpl::WaitReadersList>
weak_ptr_factory_{this};
};
////////////////////////////////////////////////////////////////////////////////
// SmartCardReaderTrackerImpl::KeepContext
// Keeps a valid `SmartCardContext` until the next
// `SmartCardReaderTrackerImpl::Start` call.
// Goes to `Uninitialized` state on timeout.
class SmartCardReaderTrackerImpl::KeepContext
: public SmartCardReaderTrackerImpl::State {
public:
KeepContext(SmartCardReaderTrackerImpl& tracker,
mojo::Remote<device::mojom::SmartCardContext> context)
: State(tracker), context_(std::move(context)) {}
~KeepContext() override = default;
std::string ToString() const override { return "KeepContext"; }
void Enter() override {
// TODO(crbug.com/1386175): start timer and go to Uninitialized on timeout.
}
void Start(StartCallback callback) override {
pending_get_readers_requests_.push(std::move(callback));
tracker_->ChangeState(std::make_unique<WaitReadersList>(
*tracker_, std::move(context_),
std::move(pending_get_readers_requests_)));
}
private:
mojo::Remote<device::mojom::SmartCardContext> context_;
base::queue<StartCallback> pending_get_readers_requests_;
};
////////////////////////////////////////////////////////////////////////////////
// SmartCardReaderTrackerImpl::Tracking
// Main state of `SmartCardReaderTracker`.
//
// `SmartCardReaderTrackerImpl::readers_` has an up to date state of all
// available smart card readers.
//
// It has a pending `SmartCardContext::GetStatusChange`request which returns
// once a change takes place.
class SmartCardReaderTrackerImpl::Tracking
: public SmartCardReaderTrackerImpl::State {
public:
Tracking(SmartCardReaderTrackerImpl& tracker,
mojo::Remote<device::mojom::SmartCardContext> context)
: State(tracker), context_(std::move(context)) {}
std::string ToString() const override { return "Tracking"; }
void Enter() override {
CHECK(tracker_->CanTrack());
std::vector<device::mojom::SmartCardReaderStateInPtr> reader_states;
// Get notified when a reader is added to the system.
if (tracker_->context_supports_reader_added_) {
auto state = device::mojom::SmartCardReaderStateIn::New(
kPnpNotification, device::mojom::SmartCardReaderStateFlags::New());
reader_states.push_back(std::move(state));
}
// Get notified when a known, existing, reader changes its state or is
// removed from the system.
for (auto const& [name, reader] : tracker_->readers_) {
reader_states.push_back(device::mojom::SmartCardReaderStateIn::New(
name, reader->CreateCurrentStateFlags()));
}
// Instead of waiting indefinitely until anything changes, wait
// for some time and then expect to receive a timeout from PC/SC.
// Useful as a way of telling whether the PC/SC backend is still "alive".
context_->GetStatusChange(
kStatusChangeTimeout, std::move(reader_states),
base::BindOnce(
&SmartCardReaderTrackerImpl::Tracking::OnGetStatusChangeDone,
weak_ptr_factory_.GetWeakPtr()));
}
void Start(StartCallback callback) override {
// TODO(crbug.com/1386175): Cancel current GetStatusChange to force a
// refresh if we are in this state for longer than MIN_REFRESH_INTERVAL.
// otherwise return cached result as below.
tracker_->GetReadersFromCache(std::move(callback));
}
private:
void OnGetStatusChangeDone(
device::mojom::SmartCardStatusChangeResultPtr result) {
if (result->is_error()) {
const device::mojom::SmartCardError error = result->get_error();
if (error == device::mojom::SmartCardError::kCancelled ||
error == device::mojom::SmartCardError::kTimeout) {
TrackChangesOrGiveUp();
} else {
tracker_->observer_list_.NotifyError(result->get_error());
tracker_->ChangeState(std::make_unique<Uninitialized>(*tracker_));
}
return;
}
tracker_->UpdateCache(result->get_reader_states());
TrackChangesOrGiveUp();
}
void TrackChangesOrGiveUp() {
if (tracker_->CanTrack()) {
tracker_->ChangeState(
std::make_unique<WaitReadersList>(*tracker_, std::move(context_)));
} else {
tracker_->ChangeState(
std::make_unique<KeepContext>(*tracker_, std::move(context_)));
}
}
mojo::Remote<device::mojom::SmartCardContext> context_;
base::WeakPtrFactory<SmartCardReaderTrackerImpl::Tracking> weak_ptr_factory_{
this};
};
////////////////////////////////////////////////////////////////////////////////
// SmartCardReaderTrackerImpl::WaitInitialReaderStatus
// It's waiting for a `SmartCardContext::GetStatusChange` request to return
// with information on the available, but unknown to the tracker, smart card
// readers.
class SmartCardReaderTrackerImpl::WaitInitialReaderStatus
: public SmartCardReaderTrackerImpl::State {
public:
explicit WaitInitialReaderStatus(
SmartCardReaderTrackerImpl& tracker,
mojo::Remote<device::mojom::SmartCardContext> context,
base::queue<StartCallback> pending_get_readers_requests,
std::vector<std::string>& new_readers)
: State(tracker),
context_(std::move(context)),
pending_get_readers_requests_(std::move(pending_get_readers_requests)),
new_readers_(new_readers) {}
std::string ToString() const override { return "WaitInitialReaderStatus"; }
void Enter() override {
CHECK(!new_readers_.empty());
std::vector<device::mojom::SmartCardReaderStateInPtr> reader_states;
for (const std::string& reader_name : new_readers_) {
CHECK_EQ(tracker_->readers_.count(reader_name), size_t(0));
auto state = device::mojom::SmartCardReaderStateIn::New();
state->reader = reader_name;
state->current_state = device::mojom::SmartCardReaderStateFlags::New(
/*unaware=*/true, false, false, false, false, false, false, false,
false, false, false);
reader_states.push_back(std::move(state));
}
context_->GetStatusChange(
base::TimeDelta::Max(), std::move(reader_states),
base::BindOnce(&SmartCardReaderTrackerImpl::WaitInitialReaderStatus::
OnGetStatusChangeDone,
weak_ptr_factory_.GetWeakPtr()));
}
void Start(StartCallback callback) override {
pending_get_readers_requests_.push(std::move(callback));
}
private:
void OnGetStatusChangeDone(
device::mojom::SmartCardStatusChangeResultPtr result) {
if (result->is_error()) {
FailRequests(pending_get_readers_requests_,
device::mojom::SmartCardError::kNoService);
tracker_->observer_list_.NotifyError(result->get_error());
tracker_->ChangeState(std::make_unique<Uninitialized>(*tracker_));
return;
}
tracker_->UpdateCache(result->get_reader_states());
while (!pending_get_readers_requests_.empty()) {
tracker_->GetReadersFromCache(
std::move(pending_get_readers_requests_.front()));
pending_get_readers_requests_.pop();
}
if (tracker_->CanTrack()) {
tracker_->ChangeState(
std::make_unique<Tracking>(*tracker_, std::move(context_)));
} else {
tracker_->ChangeState(
std::make_unique<KeepContext>(*tracker_, std::move(context_)));
}
}
mojo::Remote<device::mojom::SmartCardContext> context_;
base::queue<StartCallback> pending_get_readers_requests_;
std::vector<std::string> new_readers_;
base::WeakPtrFactory<SmartCardReaderTrackerImpl::WaitInitialReaderStatus>
weak_ptr_factory_{this};
};
////////////////////////////////////////////////////////////////////////////////
// SmartCardReaderTrackerImpl::WaitReadersList implementation
SmartCardReaderTrackerImpl::WaitReadersList::WaitReadersList(
SmartCardReaderTrackerImpl& tracker,
mojo::Remote<device::mojom::SmartCardContext> context,
base::queue<StartCallback> pending_get_readers_requests)
: State(tracker),
context_(std::move(context)),
pending_get_readers_requests_(std::move(pending_get_readers_requests)) {}
SmartCardReaderTrackerImpl::WaitReadersList::WaitReadersList(
SmartCardReaderTrackerImpl& tracker,
mojo::PendingRemote<device::mojom::SmartCardContext> pending_context,
base::queue<StartCallback> pending_get_readers_requests)
: State(tracker),
context_(std::move(pending_context)),
pending_get_readers_requests_(std::move(pending_get_readers_requests)) {}
SmartCardReaderTrackerImpl::WaitReadersList::~WaitReadersList() = default;
std::string SmartCardReaderTrackerImpl::WaitReadersList::ToString() const {
return "WaitReadersList";
}
void SmartCardReaderTrackerImpl::WaitReadersList::Enter() {
context_->ListReaders(base::BindOnce(
&SmartCardReaderTrackerImpl::WaitReadersList::OnListReadersDone,
weak_ptr_factory_.GetWeakPtr()));
}
void SmartCardReaderTrackerImpl::WaitReadersList::Start(
StartCallback callback) {
pending_get_readers_requests_.push(std::move(callback));
}
void SmartCardReaderTrackerImpl::WaitReadersList::OnListReadersDone(
device::mojom::SmartCardListReadersResultPtr result) {
std::vector<std::string> current_readers;
if (result->is_error()) {
if (result->get_error() !=
device::mojom::SmartCardError::kNoReadersAvailable) {
FailRequests(pending_get_readers_requests_, result->get_error());
tracker_->ChangeState(std::make_unique<Uninitialized>(*tracker_));
return;
}
} else {
current_readers = result->get_readers();
}
RemoveAbsentReaders(current_readers);
if (current_readers.empty()) {
ReplyNoReaders();
tracker_->ChangeState(
std::make_unique<Tracking>(*tracker_, std::move(context_)));
return;
}
std::vector<std::string> new_readers = IdentifyNewReaders(current_readers);
if (new_readers.empty()) {
// We already know about all existing readers and their states.
// The cache can be considered still up to date.
while (!pending_get_readers_requests_.empty()) {
tracker_->GetReadersFromCache(
std::move(pending_get_readers_requests_.front()));
pending_get_readers_requests_.pop();
}
// And we can go straight to Tracking (skipping WaitInitialReaderStatus).
tracker_->ChangeState(
std::make_unique<Tracking>(*tracker_, std::move(context_)));
return;
}
tracker_->ChangeState(std::make_unique<WaitInitialReaderStatus>(
*tracker_, std::move(context_), std::move(pending_get_readers_requests_),
new_readers));
}
void SmartCardReaderTrackerImpl::WaitReadersList::RemoveAbsentReaders(
const std::vector<std::string>& current_readers) {
std::unordered_set<std::string> current_readers_set;
for (const auto& reader_name : current_readers) {
current_readers_set.insert(reader_name);
}
for (auto it = tracker_->readers_.begin(); it != tracker_->readers_.end();) {
std::string reader_name = it->first;
if (current_readers_set.count(reader_name) == 0) {
std::unique_ptr<Reader> reader = std::move(it->second);
it = tracker_->readers_.erase(it);
tracker_->observer_list_.NotifyReaderRemoved(reader->blink_reader_info());
} else {
++it;
}
}
}
std::vector<std::string>
SmartCardReaderTrackerImpl::WaitReadersList::IdentifyNewReaders(
const std::vector<std::string>& current_readers) {
std::vector<std::string> new_readers;
for (const std::string& reader_name : current_readers) {
if (tracker_->readers_.count(reader_name) == 0) {
new_readers.push_back(reader_name);
}
}
return new_readers;
}
void SmartCardReaderTrackerImpl::WaitReadersList::ReplyNoReaders() {
while (!pending_get_readers_requests_.empty()) {
std::move(pending_get_readers_requests_.front())
.Run(blink::mojom::SmartCardGetReadersResult::NewReaders(
std::vector<::blink::mojom::SmartCardReaderInfoPtr>()));
pending_get_readers_requests_.pop();
}
}
////////////////////////////////////////////////////////////////////////////////
// SmartCardReaderTrackerImpl::WaitContext
// State where the `SmartCardReaderTracker` is waiting for a
// `SmartCardContextFactory::CreateContext` call to return.
class SmartCardReaderTrackerImpl::WaitContext
: public SmartCardReaderTrackerImpl::State {
public:
std::string ToString() const override { return "WaitContext"; }
WaitContext(SmartCardReaderTrackerImpl& tracker, StartCallback callback)
: State(tracker) {
pending_get_readers_requests_.push(std::move(callback));
}
void Enter() override {
tracker_->context_factory_->CreateContext(base::BindOnce(
&SmartCardReaderTrackerImpl::WaitContext::OnEstablishContextDone,
weak_ptr_factory_.GetWeakPtr()));
}
void Start(StartCallback callback) override {
pending_get_readers_requests_.push(std::move(callback));
}
private:
void OnEstablishContextDone(
device::mojom::SmartCardCreateContextResultPtr result) {
if (result->is_error()) {
FailRequests(pending_get_readers_requests_, result->get_error());
tracker_->ChangeState(std::make_unique<Uninitialized>(*tracker_));
return;
}
tracker_->ChangeState(std::make_unique<WaitReadersList>(
*tracker_, std::move(result->get_context()),
std::move(pending_get_readers_requests_)));
}
base::queue<StartCallback> pending_get_readers_requests_;
base::WeakPtrFactory<SmartCardReaderTrackerImpl::WaitContext>
weak_ptr_factory_{this};
};
////////////////////////////////////////////////////////////////////////////////
// SmartCardReaderTrackerImpl::Uninitialized implementation
std::string SmartCardReaderTrackerImpl::Uninitialized::ToString() const {
return "Uninitialized";
}
void SmartCardReaderTrackerImpl::Uninitialized::Start(StartCallback callback) {
tracker_->ChangeState(
std::make_unique<WaitContext>(*tracker_, std::move(callback)));
}
////////////////////////////////////////////////////////////////////////////////
// SmartCardReaderTrackerImpl
SmartCardReaderTrackerImpl::SmartCardReaderTrackerImpl(
mojo::PendingRemote<device::mojom::SmartCardContextFactory> context_factory,
bool context_supports_reader_added)
: state_(std::make_unique<Uninitialized>(*this)),
context_factory_(std::move(context_factory)),
context_supports_reader_added_(context_supports_reader_added) {}
SmartCardReaderTrackerImpl::~SmartCardReaderTrackerImpl() = default;
void SmartCardReaderTrackerImpl::Start(Observer* observer,
StartCallback callback) {
observer_list_.AddObserverIfMissing(observer);
state_->Start(std::move(callback));
}
void SmartCardReaderTrackerImpl::Stop(Observer* observer) {
observer_list_.RemoveObserver(observer);
// TODO(crbug.com/1386175): call state if there are no readers left. Tracking
// state should cancel its pending GetStatusChange.
}
void SmartCardReaderTrackerImpl::ChangeState(
std::unique_ptr<State> next_state) {
CHECK(next_state);
auto current_state = std::move(state_);
VLOG(1) << "ChangeState: " << current_state->ToString() << " -> "
<< next_state->ToString();
state_ = std::move(next_state);
state_->Enter();
// This method is invoked from inside `current_state`, so we postpone
// destruction to ensure it has a chance to finish its current method
// without crashing because it was deleted.
base::SequencedTaskRunner::GetCurrentDefault()->DeleteSoon(
FROM_HERE, std::move(current_state));
}
bool SmartCardReaderTrackerImpl::CanTrack() const {
return !observer_list_.empty() &&
(!readers_.empty() || context_supports_reader_added_);
}
void SmartCardReaderTrackerImpl::AddReader(
const device::mojom::SmartCardReaderStateOut& state_out) {
auto unique_reader = std::make_unique<Reader>(state_out);
const blink::mojom::SmartCardReaderInfo& reader_info =
unique_reader->blink_reader_info();
readers_[state_out.reader] = std::move(unique_reader);
if (!context_supports_reader_added_) {
// For consistency, never send it if not supported.
// Otherwise one user calling Start() would have a side
// effect on the obsevers of other users as they would
// be notified even though the didn't call Start()
// themselves.
return;
}
observer_list_.NotifyReaderAdded(reader_info);
}
void SmartCardReaderTrackerImpl::AddOrUpdateReader(
const device::mojom::SmartCardReaderStateOut& state_out) {
auto it = readers_.find(state_out.reader);
if (it == readers_.end()) {
AddReader(state_out);
} else {
std::unique_ptr<Reader>& reader = it->second;
if (reader->Update(state_out)) {
observer_list_.NotifyReaderChanged(reader->blink_reader_info());
}
}
}
void SmartCardReaderTrackerImpl::RemoveReader(
const device::mojom::SmartCardReaderStateOut& state_out) {
auto it = readers_.find(state_out.reader);
if (it == readers_.end()) {
return;
}
std::unique_ptr<Reader> reader = std::move(it->second);
readers_.erase(it);
observer_list_.NotifyReaderRemoved(reader->blink_reader_info());
}
void SmartCardReaderTrackerImpl::GetReadersFromCache(StartCallback callback) {
std::vector<::blink::mojom::SmartCardReaderInfoPtr> reader_list;
reader_list.reserve(readers_.size());
for (const auto& [_, reader] : readers_) {
reader_list.push_back(reader->blink_reader_info().Clone());
}
std::move(callback).Run(blink::mojom::SmartCardGetReadersResult::NewReaders(
std::move(reader_list)));
}
void SmartCardReaderTrackerImpl::UpdateCache(
const std::vector<device::mojom::SmartCardReaderStateOutPtr>&
reader_states) {
for (const auto& reader_state : reader_states) {
if (reader_state->reader == kPnpNotification) {
// This is not an actual reader device.
continue;
}
if (reader_state->event_state->unknown ||
reader_state->event_state->ignore ||
reader_state->event_state->unaware) {
RemoveReader(*reader_state.get());
} else {
AddOrUpdateReader(*reader_state.get());
}
}
}
} // namespace content