Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "u2f_request.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/memory/ptr_util.h" | |
| 9 #include "base/threading/thread_task_runner_handle.h" | |
| 10 #include "device/base/device_client.h" | |
| 11 #include "u2f_hid_device.h" | |
| 12 | |
| 13 namespace device { | |
| 14 | |
| 15 U2fRequest::U2fRequest(const ResponseCallback& cb) | |
| 16 : state_(State::INIT), | |
| 17 cb_(cb), | |
| 18 hid_service_observer_(this), | |
| 19 weak_factory_(this) { | |
| 20 filter_.SetUsagePage(0xf1d0); | |
| 21 } | |
| 22 | |
| 23 void U2fRequest::Transition() { | |
| 24 switch (state_) { | |
| 25 case State::IDLE: | |
| 26 IterateDevice(); | |
| 27 if (!current_device_) { | |
| 28 // No devices available | |
| 29 state_ = State::OFF; | |
| 30 break; | |
| 31 } | |
| 32 state_ = State::WINK; | |
| 33 current_device_->TryWink( | |
| 34 base::Bind(&U2fRequest::Transition, weak_factory_.GetWeakPtr())); | |
| 35 break; | |
| 36 case State::WINK: | |
| 37 state_ = State::BUSY; | |
| 38 TryDevice(); | |
| 39 default: | |
| 40 break; | |
| 41 } | |
| 42 } | |
| 43 | |
| 44 void U2fRequest::Start() { | |
| 45 if (state_ == State::INIT) { | |
| 46 state_ = State::BUSY; | |
| 47 Enumerate(); | |
| 48 } | |
| 49 } | |
| 50 | |
| 51 void U2fRequest::Enumerate() { | |
| 52 HidService* hid_service = DeviceClient::Get()->GetHidService(); | |
| 53 DCHECK(hid_service); | |
| 54 hid_service_observer_.Add(hid_service); | |
| 55 hid_service->GetDevices( | |
| 56 base::Bind(&U2fRequest::OnEnumerate, weak_factory_.GetWeakPtr())); | |
| 57 } | |
| 58 | |
| 59 void U2fRequest::OnEnumerate( | |
| 60 const std::vector<scoped_refptr<HidDeviceInfo>>& devices) { | |
| 61 for (auto device_info : devices) { | |
| 62 if (filter_.Matches(device_info)) | |
| 63 devices_.push_back(base::MakeUnique<U2fHidDevice>(device_info)); | |
| 64 } | |
| 65 | |
| 66 state_ = State::IDLE; | |
| 67 Transition(); | |
| 68 } | |
| 69 | |
| 70 void U2fRequest::OnDeviceAdded(scoped_refptr<HidDeviceInfo> device_info) { | |
| 71 // Ignore non-U2F devices | |
| 72 if (!filter_.Matches(device_info)) | |
| 73 return; | |
| 74 | |
| 75 auto device = base::MakeUnique<U2fHidDevice>(device_info); | |
| 76 devices_.push_back(std::move(device)); | |
| 77 | |
| 78 // Start the state machine if this is the only device | |
| 79 if (state_ == State::OFF) { | |
| 80 state_ = State::IDLE; | |
| 81 delay_callback_.Cancel(); | |
| 82 Transition(); | |
| 83 } | |
| 84 } | |
| 85 | |
| 86 void U2fRequest::OnDeviceRemoved(scoped_refptr<HidDeviceInfo> device_info) { | |
| 87 // Ignore non-U2F devices | |
| 88 if (!filter_.Matches(device_info)) | |
| 89 return; | |
| 90 | |
| 91 auto device = base::MakeUnique<U2fHidDevice>(device_info); | |
| 92 | |
| 93 // Check if the active device was removed | |
| 94 if (current_device_ && current_device_->GetId() == device->GetId()) { | |
| 95 current_device_ = nullptr; | |
| 96 state_ = State::IDLE; | |
| 97 Transition(); | |
| 98 return; | |
| 99 } | |
| 100 | |
| 101 // Remove the device if it exists in the devices list | |
| 102 for (auto it = devices_.begin(); it != devices_.end();) { | |
| 103 if ((*it)->GetId() == device->GetId()) { | |
| 104 it = devices_.erase(it); | |
| 105 return; | |
| 106 } else { | |
| 107 ++it; | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 // Remove the device if it exists in the attempted devices list | |
| 112 for (auto it = attempted_devices_.begin(); it != attempted_devices_.end();) { | |
| 113 if ((*it)->GetId() == device->GetId()) { | |
| 114 it = attempted_devices_.erase(it); | |
| 115 return; | |
| 116 } else { | |
| 117 ++it; | |
| 118 } | |
| 119 } | |
| 120 } | |
| 121 | |
| 122 void U2fRequest::IterateDevice() { | |
| 123 // Move active device to attempted device list | |
| 124 if (current_device_) | |
| 125 attempted_devices_.push_back(std::move(current_device_)); | |
| 126 | |
| 127 // If there is an additional device on device list, make it active. | |
| 128 // Otherwise, if all devices have been tried, move attempted devices back to | |
| 129 // the main device list. | |
| 130 if (devices_.size() > 0) { | |
| 131 current_device_ = std::move(devices_.front()); | |
| 132 devices_.pop_front(); | |
| 133 } else if (attempted_devices_.size() > 0) { | |
| 134 devices_ = std::move(attempted_devices_); | |
| 135 // After trying every device, wait 200ms before trying again | |
| 136 delay_callback_.Reset( | |
| 137 base::Bind(&U2fRequest::OnWaitComplete, weak_factory_.GetWeakPtr())); | |
| 138 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
| 139 FROM_HERE, delay_callback_.callback(), | |
| 140 base::TimeDelta::FromMilliseconds(200)); | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 void U2fRequest::OnWaitComplete() { | |
| 145 state_ = State::IDLE; | |
| 146 Transition(); | |
| 147 } | |
| 148 | |
| 149 void U2fRequest::AddDeviceForTesting(std::unique_ptr<U2fDevice> device) { | |
| 150 devices_.push_back(std::move(device)); | |
| 151 | |
| 152 // Start the state machine if this is the only device | |
| 153 if (state_ == State::OFF) { | |
| 154 state_ = State::IDLE; | |
| 155 delay_callback_.Cancel(); | |
| 156 Transition(); | |
| 157 } | |
|
Reilly Grant (use Gerrit)
2017/04/21 20:10:01
I recommend pulling this out into an AddDevice() f
Casey Piper
2017/04/21 22:55:16
Done.
| |
| 158 } | |
| 159 | |
| 160 U2fRequest::~U2fRequest() {} | |
| 161 | |
| 162 } // namespace device | |
| OLD | NEW |