| // 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 "chrome/browser/smart_card/fake_smart_card_device_service.h" |
| #include "base/containers/map_util.h" |
| |
| namespace { |
| using device::mojom::SmartCardConnectResult; |
| using device::mojom::SmartCardContext; |
| using device::mojom::SmartCardCreateContextResult; |
| using device::mojom::SmartCardCreateContextResultPtr; |
| using device::mojom::SmartCardError; |
| using device::mojom::SmartCardListReadersResult; |
| using device::mojom::SmartCardReaderStateFlags; |
| using device::mojom::SmartCardReaderStateIn; |
| using device::mojom::SmartCardReaderStateInPtr; |
| using device::mojom::SmartCardReaderStateOut; |
| using device::mojom::SmartCardReaderStateOutPtr; |
| using device::mojom::SmartCardResult; |
| using device::mojom::SmartCardStatusChangeResult; |
| using device::mojom::SmartCardSuccess; |
| |
| constexpr char kFooReader[] = "Foo Reader"; |
| constexpr char kAcmeReader[] = "Acme Reader"; |
| |
| bool StateHasChanged(const SmartCardReaderStateOut& state_out, |
| const SmartCardReaderStateIn& state_in) { |
| // Try the easy cases first: |
| |
| if (state_in.current_state->unaware) { |
| return true; |
| } |
| |
| if (state_in.current_count != state_out.event_count) { |
| return true; |
| } |
| |
| // And only then go for a full diff: |
| const SmartCardReaderStateFlags& flags_in = *state_in.current_state; |
| const SmartCardReaderStateFlags& flags_out = *state_out.event_state; |
| // clang-format off |
| return flags_in.unknown != flags_out.unknown || |
| flags_in.unavailable != flags_out.unavailable || |
| flags_in.empty != flags_out.empty || |
| flags_in.present != flags_out.present || |
| flags_in.exclusive != flags_out.exclusive || |
| flags_in.inuse != flags_out.inuse || |
| flags_in.mute != flags_out.mute || |
| flags_in.unpowered != flags_out.unpowered; |
| // clang-format on |
| } |
| } // anonymous namespace |
| |
| struct FakeSmartCardDeviceService::ReaderState { |
| bool unknown; |
| bool unavailable; |
| bool empty; |
| bool present; |
| bool exclusive; |
| bool inuse; |
| bool mute; |
| bool unpowered; |
| |
| uint16_t event_count; |
| }; |
| |
| struct FakeSmartCardDeviceService::PendingStatusChange { |
| PendingStatusChange(std::vector<SmartCardReaderStateInPtr> reader_states, |
| SmartCardContext::GetStatusChangeCallback callback) |
| : reader_states(std::move(reader_states)), |
| callback(std::move(callback)) {} |
| |
| ~PendingStatusChange() = default; |
| |
| std::vector<SmartCardReaderStateInPtr> reader_states; |
| SmartCardContext::GetStatusChangeCallback callback; |
| }; |
| |
| FakeSmartCardDeviceService::FakeSmartCardDeviceService() { |
| readers_[kFooReader] = {.empty = true}; |
| readers_[kAcmeReader] = {.present = true}; |
| } |
| |
| FakeSmartCardDeviceService::~FakeSmartCardDeviceService() = default; |
| |
| mojo::PendingRemote<device::mojom::SmartCardContextFactory> |
| FakeSmartCardDeviceService::GetSmartCardContextFactory() { |
| mojo::PendingRemote<device::mojom::SmartCardContextFactory> pending_remote; |
| context_factory_receivers_.Add( |
| this, pending_remote.InitWithNewPipeAndPassReceiver()); |
| return pending_remote; |
| } |
| |
| void FakeSmartCardDeviceService::CreateContext(CreateContextCallback callback) { |
| mojo::PendingRemote<SmartCardContext> context_remote; |
| context_receivers_.Add(this, context_remote.InitWithNewPipeAndPassReceiver()); |
| |
| std::move(callback).Run( |
| SmartCardCreateContextResult::NewContext(std::move(context_remote))); |
| } |
| |
| void FakeSmartCardDeviceService::ListReaders(ListReadersCallback callback) { |
| std::vector<std::string> names; |
| for (const auto& [name, state] : readers_) { |
| names.push_back(name); |
| } |
| std::move(callback).Run(SmartCardListReadersResult::NewReaders(names)); |
| } |
| |
| void FakeSmartCardDeviceService::GetStatusChange( |
| base::TimeDelta timeout, |
| std::vector<device::mojom::SmartCardReaderStateInPtr> reader_states, |
| GetStatusChangeCallback callback) { |
| pending_status_changes_.push_back(std::make_unique<PendingStatusChange>( |
| std::move(reader_states), std::move(callback))); |
| |
| TryResolvePendingStatusChanges(); |
| } |
| |
| void FakeSmartCardDeviceService::Cancel(CancelCallback callback) { |
| auto pending_list = std::move(pending_status_changes_); |
| for (auto& pending_change : pending_list) { |
| std::move(pending_change->callback) |
| .Run(SmartCardStatusChangeResult::NewError(SmartCardError::kCancelled)); |
| } |
| |
| std::move(callback).Run(SmartCardResult::NewSuccess(SmartCardSuccess::kOk)); |
| } |
| |
| void FakeSmartCardDeviceService::Connect( |
| const std::string& reader, |
| device::mojom::SmartCardShareMode share_mode, |
| device::mojom::SmartCardProtocolsPtr preferred_protocols, |
| mojo::PendingRemote<device::mojom::SmartCardConnectionWatcher> |
| connection_watcher, |
| ConnectCallback callback) { |
| std::move(callback).Run( |
| SmartCardConnectResult::NewError(SmartCardError::kUnresponsiveCard)); |
| } |
| |
| void FakeSmartCardDeviceService::TryResolvePendingStatusChanges() { |
| std::erase_if(pending_status_changes_, |
| [this](std::unique_ptr<PendingStatusChange>& p) { |
| return TryResolve(*p); |
| }); |
| } |
| |
| bool FakeSmartCardDeviceService::TryResolve( |
| PendingStatusChange& pending_status_change) { |
| std::vector<device::mojom::SmartCardReaderStateOutPtr> states_out; |
| |
| bool state_changed = false; |
| |
| for (const SmartCardReaderStateInPtr& state_in : |
| pending_status_change.reader_states) { |
| if (state_in->current_state->ignore) { |
| continue; |
| } |
| |
| const ReaderState* reader_state = |
| base::FindOrNull(readers_, state_in->reader); |
| auto state_out = SmartCardReaderStateOut::New(); |
| |
| if (reader_state) { |
| FillStateOut(*state_out, *state_in, *reader_state); |
| state_changed = state_out->event_state->changed; |
| } else { |
| // Inform that this reader is unknown. |
| auto flags = SmartCardReaderStateFlags::New(); |
| flags->unknown = true; |
| state_out->reader = state_in->reader; |
| state_out->event_state = std::move(flags); |
| state_changed = true; |
| } |
| |
| states_out.push_back(std::move(state_out)); |
| } |
| |
| if (state_changed) { |
| // We only finish an outstanding GetStatusChange() request if the current |
| // state of the readers is different from any of requests input states. |
| std::move(pending_status_change.callback) |
| .Run(SmartCardStatusChangeResult::NewReaderStates( |
| std::move(states_out))); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // static |
| void FakeSmartCardDeviceService::FillStateOut( |
| SmartCardReaderStateOut& state_out, |
| const SmartCardReaderStateIn& state_in, |
| const ReaderState& reader_state) { |
| state_out.reader = state_in.reader; |
| |
| state_out.event_state = SmartCardReaderStateFlags::New(); |
| |
| state_out.event_state->unknown = reader_state.unknown; |
| state_out.event_state->unavailable = reader_state.unavailable; |
| state_out.event_state->empty = reader_state.empty; |
| state_out.event_state->present = reader_state.present; |
| state_out.event_state->exclusive = reader_state.exclusive; |
| state_out.event_state->inuse = reader_state.inuse; |
| state_out.event_state->mute = reader_state.mute; |
| state_out.event_state->unpowered = reader_state.unpowered; |
| |
| state_out.event_count = reader_state.event_count; |
| |
| state_out.event_state->changed = StateHasChanged(state_out, state_in); |
| } |