blob: 8fa5933be95eacb8122ef09816501d46b2258b72 [file] [log] [blame]
shaochuane58f9c72016-08-30 22:27:08 -07001// Copyright 2016 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 "media/midi/midi_manager_winrt.h"
6
qiankun.miao53f2d662016-09-02 17:44:08 -07007#pragma warning(disable : 4467)
shaochuan80f1fba2016-09-01 20:44:51 -07008
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +00009#include <windows.h>
shaochuan4eff30e2016-09-09 01:24:14 -070010
11#include <cfgmgr32.h>
shaochuane58f9c72016-08-30 22:27:08 -070012#include <comdef.h>
shaochuan4eff30e2016-09-09 01:24:14 -070013#include <devpkey.h>
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000014#include <initguid.h>
robliao048b1572017-04-21 12:46:39 -070015#include <objbase.h>
shaochuane58f9c72016-08-30 22:27:08 -070016#include <robuffer.h>
17#include <windows.devices.enumeration.h>
18#include <windows.devices.midi.h>
Robert Liao4a680c32017-10-18 19:10:01 +000019#include <wrl/client.h>
shaochuane58f9c72016-08-30 22:27:08 -070020#include <wrl/event.h>
21
22#include <iomanip>
23#include <unordered_map>
24#include <unordered_set>
25
26#include "base/bind.h"
shaochuan9ff63b82016-09-01 01:58:44 -070027#include "base/scoped_generic.h"
shaochuan17bc4a02016-09-06 01:42:12 -070028#include "base/strings/string_util.h"
shaochuane58f9c72016-08-30 22:27:08 -070029#include "base/strings/utf_string_conversions.h"
shaochuane58f9c72016-08-30 22:27:08 -070030#include "base/timer/timer.h"
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000031#include "base/win/core_winrt_util.h"
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000032#include "base/win/scoped_hstring.h"
junweifuf51c5a02017-11-03 06:37:09 +000033#include "base/win/winrt_storage_util.h"
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000034#include "media/midi/midi_service.h"
35#include "media/midi/task_service.h"
shaochuane58f9c72016-08-30 22:27:08 -070036
shaochuane58f9c72016-08-30 22:27:08 -070037namespace midi {
38namespace {
39
40namespace WRL = Microsoft::WRL;
41
42using namespace ABI::Windows::Devices::Enumeration;
43using namespace ABI::Windows::Devices::Midi;
44using namespace ABI::Windows::Foundation;
45using namespace ABI::Windows::Storage::Streams;
46
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000047using base::win::ScopedHString;
junweifua8cea852017-10-17 06:21:16 +000048using base::win::GetActivationFactory;
toyoshimec2570a2016-10-21 02:15:27 -070049using mojom::PortState;
toyoshim2f3a48f2016-10-17 01:54:13 -070050using mojom::Result;
shaochuane58f9c72016-08-30 22:27:08 -070051
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000052enum {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +000053 kDefaultTaskRunner = TaskService::kDefaultRunnerId,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000054 kComTaskRunner
55};
56
shaochuane58f9c72016-08-30 22:27:08 -070057// Helpers for printing HRESULTs.
58struct PrintHr {
59 PrintHr(HRESULT hr) : hr(hr) {}
60 HRESULT hr;
61};
62
63std::ostream& operator<<(std::ostream& os, const PrintHr& phr) {
64 std::ios_base::fmtflags ff = os.flags();
65 os << _com_error(phr.hr).ErrorMessage() << " (0x" << std::hex
66 << std::uppercase << std::setfill('0') << std::setw(8) << phr.hr << ")";
67 os.flags(ff);
68 return os;
69}
70
shaochuane58f9c72016-08-30 22:27:08 -070071template <typename T>
72std::string GetIdString(T* obj) {
shaochuan80f1fba2016-09-01 20:44:51 -070073 HSTRING result;
74 HRESULT hr = obj->get_Id(&result);
75 if (FAILED(hr)) {
76 VLOG(1) << "get_Id failed: " << PrintHr(hr);
77 return std::string();
78 }
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000079 return ScopedHString(result).GetAsUTF8();
shaochuane58f9c72016-08-30 22:27:08 -070080}
81
82template <typename T>
83std::string GetDeviceIdString(T* obj) {
shaochuan80f1fba2016-09-01 20:44:51 -070084 HSTRING result;
85 HRESULT hr = obj->get_DeviceId(&result);
86 if (FAILED(hr)) {
87 VLOG(1) << "get_DeviceId failed: " << PrintHr(hr);
88 return std::string();
89 }
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000090 return ScopedHString(result).GetAsUTF8();
shaochuane58f9c72016-08-30 22:27:08 -070091}
92
93std::string GetNameString(IDeviceInformation* info) {
shaochuan80f1fba2016-09-01 20:44:51 -070094 HSTRING result;
95 HRESULT hr = info->get_Name(&result);
96 if (FAILED(hr)) {
97 VLOG(1) << "get_Name failed: " << PrintHr(hr);
98 return std::string();
99 }
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000100 return ScopedHString(result).GetAsUTF8();
shaochuane58f9c72016-08-30 22:27:08 -0700101}
102
shaochuan110262b2016-08-31 02:15:16 -0700103// Checks if given DeviceInformation represent a Microsoft GS Wavetable Synth
104// instance.
105bool IsMicrosoftSynthesizer(IDeviceInformation* info) {
Robert Liao4a680c32017-10-18 19:10:01 +0000106 WRL::ComPtr<IMidiSynthesizerStatics> midi_synthesizer_statics;
junweifua8cea852017-10-17 06:21:16 +0000107 HRESULT hr =
108 GetActivationFactory<IMidiSynthesizerStatics,
109 RuntimeClass_Windows_Devices_Midi_MidiSynthesizer>(
110 &midi_synthesizer_statics);
111 if (FAILED(hr)) {
112 VLOG(1) << "IMidiSynthesizerStatics factory failed: " << PrintHr(hr);
113 return false;
114 }
shaochuan110262b2016-08-31 02:15:16 -0700115 boolean result = FALSE;
junweifua8cea852017-10-17 06:21:16 +0000116 hr = midi_synthesizer_statics->IsSynthesizer(info, &result);
shaochuan110262b2016-08-31 02:15:16 -0700117 VLOG_IF(1, FAILED(hr)) << "IsSynthesizer failed: " << PrintHr(hr);
118 return result != FALSE;
119}
120
shaochuan4eff30e2016-09-09 01:24:14 -0700121void GetDevPropString(DEVINST handle,
122 const DEVPROPKEY* devprop_key,
123 std::string* out) {
124 DEVPROPTYPE devprop_type;
125 unsigned long buffer_size = 0;
shaochuan17bc4a02016-09-06 01:42:12 -0700126
shaochuan4eff30e2016-09-09 01:24:14 -0700127 // Retrieve |buffer_size| and allocate buffer later for receiving data.
128 CONFIGRET cr = CM_Get_DevNode_Property(handle, devprop_key, &devprop_type,
129 nullptr, &buffer_size, 0);
130 if (cr != CR_BUFFER_SMALL) {
131 // Here we print error codes in hex instead of using PrintHr() with
132 // HRESULT_FROM_WIN32() and CM_MapCrToWin32Err(), since only a minor set of
133 // CONFIGRET values are mapped to Win32 errors. Same for following VLOG()s.
134 VLOG(1) << "CM_Get_DevNode_Property failed: CONFIGRET 0x" << std::hex << cr;
135 return;
shaochuan17bc4a02016-09-06 01:42:12 -0700136 }
shaochuan4eff30e2016-09-09 01:24:14 -0700137 if (devprop_type != DEVPROP_TYPE_STRING) {
138 VLOG(1) << "CM_Get_DevNode_Property returns wrong data type, "
139 << "expected DEVPROP_TYPE_STRING";
140 return;
141 }
shaochuan17bc4a02016-09-06 01:42:12 -0700142
shaochuan4eff30e2016-09-09 01:24:14 -0700143 std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
144
145 // Receive property data.
146 cr = CM_Get_DevNode_Property(handle, devprop_key, &devprop_type, buffer.get(),
147 &buffer_size, 0);
148 if (cr != CR_SUCCESS)
149 VLOG(1) << "CM_Get_DevNode_Property failed: CONFIGRET 0x" << std::hex << cr;
150 else
151 *out = base::WideToUTF8(reinterpret_cast<base::char16*>(buffer.get()));
152}
shaochuan17bc4a02016-09-06 01:42:12 -0700153
154// Retrieves manufacturer (provider) and version information of underlying
shaochuan4eff30e2016-09-09 01:24:14 -0700155// device driver through PnP Configuration Manager, given device (interface) ID
156// provided by WinRT. |out_manufacturer| and |out_driver_version| won't be
157// modified if retrieval fails.
shaochuan17bc4a02016-09-06 01:42:12 -0700158//
159// Device instance ID is extracted from device (interface) ID provided by WinRT
160// APIs, for example from the following interface ID:
161// \\?\SWD#MMDEVAPI#MIDII_60F39FCA.P_0002#{504be32c-ccf6-4d2c-b73f-6f8b3747e22b}
162// we extract the device instance ID: SWD\MMDEVAPI\MIDII_60F39FCA.P_0002
shaochuan4eff30e2016-09-09 01:24:14 -0700163//
164// However the extracted device instance ID represent a "software device"
165// provided by Microsoft, which is an interface on top of the hardware for each
166// input/output port. Therefore we further locate its parent device, which is
167// the actual hardware device, for driver information.
shaochuan17bc4a02016-09-06 01:42:12 -0700168void GetDriverInfoFromDeviceId(const std::string& dev_id,
169 std::string* out_manufacturer,
170 std::string* out_driver_version) {
171 base::string16 dev_instance_id =
172 base::UTF8ToWide(dev_id.substr(4, dev_id.size() - 43));
173 base::ReplaceChars(dev_instance_id, L"#", L"\\", &dev_instance_id);
174
shaochuan4eff30e2016-09-09 01:24:14 -0700175 DEVINST dev_instance_handle;
176 CONFIGRET cr = CM_Locate_DevNode(&dev_instance_handle, &dev_instance_id[0],
177 CM_LOCATE_DEVNODE_NORMAL);
178 if (cr != CR_SUCCESS) {
179 VLOG(1) << "CM_Locate_DevNode failed: CONFIGRET 0x" << std::hex << cr;
shaochuan17bc4a02016-09-06 01:42:12 -0700180 return;
181 }
182
shaochuan4eff30e2016-09-09 01:24:14 -0700183 DEVINST parent_handle;
184 cr = CM_Get_Parent(&parent_handle, dev_instance_handle, 0);
185 if (cr != CR_SUCCESS) {
186 VLOG(1) << "CM_Get_Parent failed: CONFIGRET 0x" << std::hex << cr;
shaochuan17bc4a02016-09-06 01:42:12 -0700187 return;
188 }
189
shaochuan4eff30e2016-09-09 01:24:14 -0700190 GetDevPropString(parent_handle, &DEVPKEY_Device_DriverProvider,
191 out_manufacturer);
192 GetDevPropString(parent_handle, &DEVPKEY_Device_DriverVersion,
193 out_driver_version);
shaochuan17bc4a02016-09-06 01:42:12 -0700194}
195
shaochuane58f9c72016-08-30 22:27:08 -0700196// Tokens with value = 0 are considered invalid (as in <wrl/event.h>).
197const int64_t kInvalidTokenValue = 0;
198
199template <typename InterfaceType>
200struct MidiPort {
201 MidiPort() = default;
202
203 uint32_t index;
Robert Liao4a680c32017-10-18 19:10:01 +0000204 WRL::ComPtr<InterfaceType> handle;
shaochuane58f9c72016-08-30 22:27:08 -0700205 EventRegistrationToken token_MessageReceived;
206
207 private:
208 DISALLOW_COPY_AND_ASSIGN(MidiPort);
209};
210
211} // namespace
212
213template <typename InterfaceType,
214 typename RuntimeType,
215 typename StaticsInterfaceType,
216 base::char16 const* runtime_class_id>
217class MidiManagerWinrt::MidiPortManager {
218 public:
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000219 // MidiPortManager instances should be constructed on the kComTaskRunner.
shaochuane58f9c72016-08-30 22:27:08 -0700220 MidiPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000221 : midi_service_(midi_manager->service()), midi_manager_(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700222
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000223 virtual ~MidiPortManager() {
224 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
225 }
shaochuane58f9c72016-08-30 22:27:08 -0700226
227 bool StartWatcher() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000228 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700229
junweifua8cea852017-10-17 06:21:16 +0000230 HRESULT hr = GetActivationFactory<StaticsInterfaceType, runtime_class_id>(
231 &midi_port_statics_);
232 if (FAILED(hr)) {
233 VLOG(1) << "StaticsInterfaceType factory failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700234 return false;
junweifua8cea852017-10-17 06:21:16 +0000235 }
shaochuane58f9c72016-08-30 22:27:08 -0700236
237 HSTRING device_selector = nullptr;
238 hr = midi_port_statics_->GetDeviceSelector(&device_selector);
239 if (FAILED(hr)) {
240 VLOG(1) << "GetDeviceSelector failed: " << PrintHr(hr);
241 return false;
242 }
243
Robert Liao4a680c32017-10-18 19:10:01 +0000244 WRL::ComPtr<IDeviceInformationStatics> dev_info_statics;
junweifua8cea852017-10-17 06:21:16 +0000245 hr = GetActivationFactory<
shaochuane58f9c72016-08-30 22:27:08 -0700246 IDeviceInformationStatics,
junweifua8cea852017-10-17 06:21:16 +0000247 RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>(
248 &dev_info_statics);
249 if (FAILED(hr)) {
250 VLOG(1) << "IDeviceInformationStatics failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700251 return false;
junweifua8cea852017-10-17 06:21:16 +0000252 }
shaochuane58f9c72016-08-30 22:27:08 -0700253
254 hr = dev_info_statics->CreateWatcherAqsFilter(device_selector,
robliao8d08e692017-05-11 10:14:00 -0700255 watcher_.GetAddressOf());
shaochuane58f9c72016-08-30 22:27:08 -0700256 if (FAILED(hr)) {
257 VLOG(1) << "CreateWatcherAqsFilter failed: " << PrintHr(hr);
258 return false;
259 }
260
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000261 // Register callbacks to WinRT that post state-modifying tasks back to
262 // kComTaskRunner. All posted tasks run only during the MidiPortManager
263 // instance is alive. This is ensured by MidiManagerWinrt by calling
264 // UnbindInstance() before destructing any MidiPortManager instance. Thus
265 // we can handle raw pointers safely in the following blocks.
266 MidiPortManager* port_manager = this;
267 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700268
269 hr = watcher_->add_Added(
270 WRL::Callback<ITypedEventHandler<DeviceWatcher*, DeviceInformation*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000271 [port_manager, task_service](IDeviceWatcher* watcher,
272 IDeviceInformation* info) {
shaochuanc2894522016-09-20 01:10:50 -0700273 if (!info) {
274 VLOG(1) << "DeviceWatcher.Added callback provides null "
275 "pointer, ignoring";
276 return S_OK;
277 }
278
shaochuan110262b2016-08-31 02:15:16 -0700279 // Disable Microsoft GS Wavetable Synth due to security reasons.
280 // http://crbug.com/499279
281 if (IsMicrosoftSynthesizer(info))
282 return S_OK;
283
shaochuane58f9c72016-08-30 22:27:08 -0700284 std::string dev_id = GetIdString(info),
285 dev_name = GetNameString(info);
286
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000287 task_service->PostBoundTask(
288 kComTaskRunner, base::BindOnce(&MidiPortManager::OnAdded,
289 base::Unretained(port_manager),
290 dev_id, dev_name));
shaochuane58f9c72016-08-30 22:27:08 -0700291
292 return S_OK;
293 })
294 .Get(),
295 &token_Added_);
296 if (FAILED(hr)) {
297 VLOG(1) << "add_Added failed: " << PrintHr(hr);
298 return false;
299 }
300
301 hr = watcher_->add_EnumerationCompleted(
302 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000303 [port_manager, task_service](IDeviceWatcher* watcher,
304 IInspectable* insp) {
305 task_service->PostBoundTask(
306 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000307 base::BindOnce(&MidiPortManager::OnEnumerationCompleted,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000308 base::Unretained(port_manager)));
shaochuane58f9c72016-08-30 22:27:08 -0700309
310 return S_OK;
311 })
312 .Get(),
313 &token_EnumerationCompleted_);
314 if (FAILED(hr)) {
315 VLOG(1) << "add_EnumerationCompleted failed: " << PrintHr(hr);
316 return false;
317 }
318
319 hr = watcher_->add_Removed(
320 WRL::Callback<
321 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000322 [port_manager, task_service](IDeviceWatcher* watcher,
323 IDeviceInformationUpdate* update) {
shaochuanc2894522016-09-20 01:10:50 -0700324 if (!update) {
325 VLOG(1) << "DeviceWatcher.Removed callback provides null "
326 "pointer, ignoring";
327 return S_OK;
328 }
329
shaochuane58f9c72016-08-30 22:27:08 -0700330 std::string dev_id = GetIdString(update);
331
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000332 task_service->PostBoundTask(
333 kComTaskRunner,
334 base::BindOnce(&MidiPortManager::OnRemoved,
335 base::Unretained(port_manager), dev_id));
shaochuane58f9c72016-08-30 22:27:08 -0700336
337 return S_OK;
338 })
339 .Get(),
340 &token_Removed_);
341 if (FAILED(hr)) {
342 VLOG(1) << "add_Removed failed: " << PrintHr(hr);
343 return false;
344 }
345
346 hr = watcher_->add_Stopped(
347 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
348 [](IDeviceWatcher* watcher, IInspectable* insp) {
349 // Placeholder, does nothing for now.
350 return S_OK;
351 })
352 .Get(),
353 &token_Stopped_);
354 if (FAILED(hr)) {
355 VLOG(1) << "add_Stopped failed: " << PrintHr(hr);
356 return false;
357 }
358
359 hr = watcher_->add_Updated(
360 WRL::Callback<
361 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
362 [](IDeviceWatcher* watcher, IDeviceInformationUpdate* update) {
363 // TODO(shaochuan): Check for fields to be updated here.
364 return S_OK;
365 })
366 .Get(),
367 &token_Updated_);
368 if (FAILED(hr)) {
369 VLOG(1) << "add_Updated failed: " << PrintHr(hr);
370 return false;
371 }
372
373 hr = watcher_->Start();
374 if (FAILED(hr)) {
375 VLOG(1) << "Start failed: " << PrintHr(hr);
376 return false;
377 }
378
379 is_initialized_ = true;
380 return true;
381 }
382
383 void StopWatcher() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000384 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700385
386 HRESULT hr;
387
388 for (const auto& entry : ports_)
389 RemovePortEventHandlers(entry.second.get());
390
391 if (token_Added_.value != kInvalidTokenValue) {
392 hr = watcher_->remove_Added(token_Added_);
393 VLOG_IF(1, FAILED(hr)) << "remove_Added failed: " << PrintHr(hr);
394 token_Added_.value = kInvalidTokenValue;
395 }
396 if (token_EnumerationCompleted_.value != kInvalidTokenValue) {
397 hr = watcher_->remove_EnumerationCompleted(token_EnumerationCompleted_);
398 VLOG_IF(1, FAILED(hr)) << "remove_EnumerationCompleted failed: "
399 << PrintHr(hr);
400 token_EnumerationCompleted_.value = kInvalidTokenValue;
401 }
402 if (token_Removed_.value != kInvalidTokenValue) {
403 hr = watcher_->remove_Removed(token_Removed_);
404 VLOG_IF(1, FAILED(hr)) << "remove_Removed failed: " << PrintHr(hr);
405 token_Removed_.value = kInvalidTokenValue;
406 }
407 if (token_Stopped_.value != kInvalidTokenValue) {
408 hr = watcher_->remove_Stopped(token_Stopped_);
409 VLOG_IF(1, FAILED(hr)) << "remove_Stopped failed: " << PrintHr(hr);
410 token_Stopped_.value = kInvalidTokenValue;
411 }
412 if (token_Updated_.value != kInvalidTokenValue) {
413 hr = watcher_->remove_Updated(token_Updated_);
414 VLOG_IF(1, FAILED(hr)) << "remove_Updated failed: " << PrintHr(hr);
415 token_Updated_.value = kInvalidTokenValue;
416 }
417
418 if (is_initialized_) {
419 hr = watcher_->Stop();
420 VLOG_IF(1, FAILED(hr)) << "Stop failed: " << PrintHr(hr);
421 is_initialized_ = false;
422 }
423 }
424
425 MidiPort<InterfaceType>* GetPortByDeviceId(std::string dev_id) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000426 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700427 CHECK(is_initialized_);
428
429 auto it = ports_.find(dev_id);
430 if (it == ports_.end())
431 return nullptr;
432 return it->second.get();
433 }
434
435 MidiPort<InterfaceType>* GetPortByIndex(uint32_t port_index) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000436 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700437 CHECK(is_initialized_);
438
439 return GetPortByDeviceId(port_ids_[port_index]);
440 }
441
442 protected:
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000443 // Points to the MidiService instance, which is expected to outlive the
shaochuane58f9c72016-08-30 22:27:08 -0700444 // MidiPortManager instance.
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000445 MidiService* midi_service_;
446
447 // Points to the MidiManagerWinrt instance, which is safe to be accessed
448 // from tasks that are invoked by TaskService.
shaochuane58f9c72016-08-30 22:27:08 -0700449 MidiManagerWinrt* midi_manager_;
450
shaochuane58f9c72016-08-30 22:27:08 -0700451 private:
452 // DeviceWatcher callbacks:
453 void OnAdded(std::string dev_id, std::string dev_name) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000454 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700455 CHECK(is_initialized_);
456
shaochuane58f9c72016-08-30 22:27:08 -0700457 port_names_[dev_id] = dev_name;
458
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000459 ScopedHString dev_id_hstring = ScopedHString::Create(dev_id);
shaochuan9ff63b82016-09-01 01:58:44 -0700460 if (!dev_id_hstring.is_valid())
shaochuane58f9c72016-08-30 22:27:08 -0700461 return;
shaochuane58f9c72016-08-30 22:27:08 -0700462
463 IAsyncOperation<RuntimeType*>* async_op;
464
shaochuan9ff63b82016-09-01 01:58:44 -0700465 HRESULT hr =
466 midi_port_statics_->FromIdAsync(dev_id_hstring.get(), &async_op);
shaochuane58f9c72016-08-30 22:27:08 -0700467 if (FAILED(hr)) {
468 VLOG(1) << "FromIdAsync failed: " << PrintHr(hr);
469 return;
470 }
471
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000472 MidiPortManager* port_manager = this;
473 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700474
475 hr = async_op->put_Completed(
476 WRL::Callback<IAsyncOperationCompletedHandler<RuntimeType*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000477 [port_manager, task_service](
478 IAsyncOperation<RuntimeType*>* async_op, AsyncStatus status) {
shaochuane58f9c72016-08-30 22:27:08 -0700479 // A reference to |async_op| is kept in |async_ops_|, safe to pass
480 // outside.
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000481 task_service->PostBoundTask(
482 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000483 base::BindOnce(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000484 &MidiPortManager::OnCompletedGetPortFromIdAsync,
tzik9f09f552018-02-21 12:56:03 +0000485 base::Unretained(port_manager),
486 base::Unretained(async_op)));
shaochuane58f9c72016-08-30 22:27:08 -0700487
488 return S_OK;
489 })
490 .Get());
491 if (FAILED(hr)) {
492 VLOG(1) << "put_Completed failed: " << PrintHr(hr);
493 return;
494 }
495
496 // Keep a reference to incompleted |async_op| for releasing later.
497 async_ops_.insert(async_op);
498 }
499
500 void OnEnumerationCompleted() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000501 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700502 CHECK(is_initialized_);
503
504 if (async_ops_.empty())
505 midi_manager_->OnPortManagerReady();
506 else
507 enumeration_completed_not_ready_ = true;
508 }
509
510 void OnRemoved(std::string dev_id) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000511 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700512 CHECK(is_initialized_);
513
shaochuan110262b2016-08-31 02:15:16 -0700514 // Note: in case Microsoft GS Wavetable Synth triggers this event for some
515 // reason, it will be ignored here with log emitted.
shaochuane58f9c72016-08-30 22:27:08 -0700516 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
517 if (!port) {
518 VLOG(1) << "Removing non-existent port " << dev_id;
519 return;
520 }
521
toyoshimec2570a2016-10-21 02:15:27 -0700522 SetPortState(port->index, PortState::DISCONNECTED);
shaochuane58f9c72016-08-30 22:27:08 -0700523
524 RemovePortEventHandlers(port);
525 port->handle = nullptr;
526 }
527
shaochuanc2894522016-09-20 01:10:50 -0700528 void OnCompletedGetPortFromIdAsync(IAsyncOperation<RuntimeType*>* async_op) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000529 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700530 CHECK(is_initialized_);
531
shaochuanc2894522016-09-20 01:10:50 -0700532 InterfaceType* handle = nullptr;
533 HRESULT hr = async_op->GetResults(&handle);
534 if (FAILED(hr)) {
535 VLOG(1) << "GetResults failed: " << PrintHr(hr);
536 return;
537 }
538
539 // Manually release COM interface to completed |async_op|.
540 auto it = async_ops_.find(async_op);
541 CHECK(it != async_ops_.end());
542 (*it)->Release();
543 async_ops_.erase(it);
544
545 if (!handle) {
546 VLOG(1) << "Midi{In,Out}Port.FromIdAsync callback provides null pointer, "
547 "ignoring";
548 return;
549 }
550
shaochuane58f9c72016-08-30 22:27:08 -0700551 EventRegistrationToken token = {kInvalidTokenValue};
552 if (!RegisterOnMessageReceived(handle, &token))
553 return;
554
555 std::string dev_id = GetDeviceIdString(handle);
556
557 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
558
559 if (port == nullptr) {
shaochuan17bc4a02016-09-06 01:42:12 -0700560 std::string manufacturer = "Unknown", driver_version = "Unknown";
561 GetDriverInfoFromDeviceId(dev_id, &manufacturer, &driver_version);
562
563 AddPort(MidiPortInfo(dev_id, manufacturer, port_names_[dev_id],
toyoshimec2570a2016-10-21 02:15:27 -0700564 driver_version, PortState::OPENED));
shaochuane58f9c72016-08-30 22:27:08 -0700565
566 port = new MidiPort<InterfaceType>;
567 port->index = static_cast<uint32_t>(port_ids_.size());
568
569 ports_[dev_id].reset(port);
570 port_ids_.push_back(dev_id);
571 } else {
toyoshimec2570a2016-10-21 02:15:27 -0700572 SetPortState(port->index, PortState::CONNECTED);
shaochuane58f9c72016-08-30 22:27:08 -0700573 }
574
575 port->handle = handle;
576 port->token_MessageReceived = token;
577
shaochuane58f9c72016-08-30 22:27:08 -0700578 if (enumeration_completed_not_ready_ && async_ops_.empty()) {
579 midi_manager_->OnPortManagerReady();
580 enumeration_completed_not_ready_ = false;
581 }
582 }
583
584 // Overrided by MidiInPortManager to listen to input ports.
585 virtual bool RegisterOnMessageReceived(InterfaceType* handle,
586 EventRegistrationToken* p_token) {
587 return true;
588 }
589
590 // Overrided by MidiInPortManager to remove MessageReceived event handler.
591 virtual void RemovePortEventHandlers(MidiPort<InterfaceType>* port) {}
592
593 // Calls midi_manager_->Add{Input,Output}Port.
594 virtual void AddPort(MidiPortInfo info) = 0;
595
596 // Calls midi_manager_->Set{Input,Output}PortState.
toyoshimec2570a2016-10-21 02:15:27 -0700597 virtual void SetPortState(uint32_t port_index, PortState state) = 0;
shaochuane58f9c72016-08-30 22:27:08 -0700598
shaochuane58f9c72016-08-30 22:27:08 -0700599 // Midi{In,Out}PortStatics instance.
Robert Liao4a680c32017-10-18 19:10:01 +0000600 WRL::ComPtr<StaticsInterfaceType> midi_port_statics_;
shaochuane58f9c72016-08-30 22:27:08 -0700601
602 // DeviceWatcher instance and event registration tokens for unsubscribing
603 // events in destructor.
Robert Liao4a680c32017-10-18 19:10:01 +0000604 WRL::ComPtr<IDeviceWatcher> watcher_;
shaochuane58f9c72016-08-30 22:27:08 -0700605 EventRegistrationToken token_Added_ = {kInvalidTokenValue},
606 token_EnumerationCompleted_ = {kInvalidTokenValue},
607 token_Removed_ = {kInvalidTokenValue},
608 token_Stopped_ = {kInvalidTokenValue},
609 token_Updated_ = {kInvalidTokenValue};
610
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000611 // All manipulations to these fields should be done on kComTaskRunner.
shaochuane58f9c72016-08-30 22:27:08 -0700612 std::unordered_map<std::string, std::unique_ptr<MidiPort<InterfaceType>>>
613 ports_;
614 std::vector<std::string> port_ids_;
615 std::unordered_map<std::string, std::string> port_names_;
616
617 // Keeps AsyncOperation references before the operation completes. Note that
618 // raw pointers are used here and the COM interfaces should be released
619 // manually.
620 std::unordered_set<IAsyncOperation<RuntimeType*>*> async_ops_;
621
622 // Set when device enumeration is completed but OnPortManagerReady() is not
623 // called since some ports are not yet ready (i.e. |async_ops_| is not empty).
624 // In such cases, OnPortManagerReady() will be called in
625 // OnCompletedGetPortFromIdAsync() when the last pending port is ready.
626 bool enumeration_completed_not_ready_ = false;
627
628 // Set if the instance is initialized without error. Should be checked in all
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000629 // methods on kComTaskRunner except StartWatcher().
shaochuane58f9c72016-08-30 22:27:08 -0700630 bool is_initialized_ = false;
631};
632
633class MidiManagerWinrt::MidiInPortManager final
634 : public MidiPortManager<IMidiInPort,
635 MidiInPort,
636 IMidiInPortStatics,
637 RuntimeClass_Windows_Devices_Midi_MidiInPort> {
638 public:
639 MidiInPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000640 : MidiPortManager(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700641
642 private:
643 // MidiPortManager overrides:
644 bool RegisterOnMessageReceived(IMidiInPort* handle,
645 EventRegistrationToken* p_token) override {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000646 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700647
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000648 MidiInPortManager* port_manager = this;
649 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700650
651 HRESULT hr = handle->add_MessageReceived(
652 WRL::Callback<
653 ITypedEventHandler<MidiInPort*, MidiMessageReceivedEventArgs*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000654 [port_manager, task_service](IMidiInPort* handle,
655 IMidiMessageReceivedEventArgs* args) {
shaochuane58f9c72016-08-30 22:27:08 -0700656 const base::TimeTicks now = base::TimeTicks::Now();
657
658 std::string dev_id = GetDeviceIdString(handle);
659
Robert Liao4a680c32017-10-18 19:10:01 +0000660 WRL::ComPtr<IMidiMessage> message;
robliao8d08e692017-05-11 10:14:00 -0700661 HRESULT hr = args->get_Message(message.GetAddressOf());
shaochuane58f9c72016-08-30 22:27:08 -0700662 if (FAILED(hr)) {
663 VLOG(1) << "get_Message failed: " << PrintHr(hr);
664 return hr;
665 }
666
Robert Liao4a680c32017-10-18 19:10:01 +0000667 WRL::ComPtr<IBuffer> buffer;
robliao8d08e692017-05-11 10:14:00 -0700668 hr = message->get_RawData(buffer.GetAddressOf());
shaochuane58f9c72016-08-30 22:27:08 -0700669 if (FAILED(hr)) {
670 VLOG(1) << "get_RawData failed: " << PrintHr(hr);
671 return hr;
672 }
673
674 uint8_t* p_buffer_data = nullptr;
junweifuf51c5a02017-11-03 06:37:09 +0000675 uint32_t data_length = 0;
676 hr = base::win::GetPointerToBufferData(
677 buffer.Get(), &p_buffer_data, &data_length);
shaochuane58f9c72016-08-30 22:27:08 -0700678 if (FAILED(hr))
679 return hr;
680
shaochuane58f9c72016-08-30 22:27:08 -0700681 std::vector<uint8_t> data(p_buffer_data,
682 p_buffer_data + data_length);
683
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000684 task_service->PostBoundTask(
685 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000686 base::BindOnce(&MidiInPortManager::OnMessageReceived,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000687 base::Unretained(port_manager), dev_id, data,
688 now));
shaochuane58f9c72016-08-30 22:27:08 -0700689
690 return S_OK;
691 })
692 .Get(),
693 p_token);
694 if (FAILED(hr)) {
695 VLOG(1) << "add_MessageReceived failed: " << PrintHr(hr);
696 return false;
697 }
698
699 return true;
700 }
701
702 void RemovePortEventHandlers(MidiPort<IMidiInPort>* port) override {
703 if (!(port->handle &&
704 port->token_MessageReceived.value != kInvalidTokenValue))
705 return;
706
707 HRESULT hr =
708 port->handle->remove_MessageReceived(port->token_MessageReceived);
709 VLOG_IF(1, FAILED(hr)) << "remove_MessageReceived failed: " << PrintHr(hr);
710 port->token_MessageReceived.value = kInvalidTokenValue;
711 }
712
713 void AddPort(MidiPortInfo info) final { midi_manager_->AddInputPort(info); }
714
toyoshimec2570a2016-10-21 02:15:27 -0700715 void SetPortState(uint32_t port_index, PortState state) final {
shaochuane58f9c72016-08-30 22:27:08 -0700716 midi_manager_->SetInputPortState(port_index, state);
717 }
718
shaochuane58f9c72016-08-30 22:27:08 -0700719 // Callback on receiving MIDI input message.
720 void OnMessageReceived(std::string dev_id,
721 std::vector<uint8_t> data,
722 base::TimeTicks time) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000723 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700724
725 MidiPort<IMidiInPort>* port = GetPortByDeviceId(dev_id);
726 CHECK(port);
727
728 midi_manager_->ReceiveMidiData(port->index, &data[0], data.size(), time);
729 }
730
shaochuane58f9c72016-08-30 22:27:08 -0700731 DISALLOW_COPY_AND_ASSIGN(MidiInPortManager);
732};
733
734class MidiManagerWinrt::MidiOutPortManager final
735 : public MidiPortManager<IMidiOutPort,
736 IMidiOutPort,
737 IMidiOutPortStatics,
738 RuntimeClass_Windows_Devices_Midi_MidiOutPort> {
739 public:
740 MidiOutPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000741 : MidiPortManager(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700742
743 private:
744 // MidiPortManager overrides:
745 void AddPort(MidiPortInfo info) final { midi_manager_->AddOutputPort(info); }
746
toyoshimec2570a2016-10-21 02:15:27 -0700747 void SetPortState(uint32_t port_index, PortState state) final {
shaochuane58f9c72016-08-30 22:27:08 -0700748 midi_manager_->SetOutputPortState(port_index, state);
749 }
750
shaochuane58f9c72016-08-30 22:27:08 -0700751 DISALLOW_COPY_AND_ASSIGN(MidiOutPortManager);
752};
753
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000754namespace {
755
756// FinalizeOnComRunner() run on kComTaskRunner even after the MidiManager
757// instance destruction.
758void FinalizeOnComRunner(
759 std::unique_ptr<MidiManagerWinrt::MidiInPortManager> port_manager_in,
760 std::unique_ptr<MidiManagerWinrt::MidiOutPortManager> port_manager_out) {
761 if (port_manager_in)
762 port_manager_in->StopWatcher();
763
764 if (port_manager_out)
765 port_manager_out->StopWatcher();
766}
767
768} // namespace
769
toyoshimf4d61522017-02-10 02:03:32 -0800770MidiManagerWinrt::MidiManagerWinrt(MidiService* service)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000771 : MidiManager(service) {}
shaochuane58f9c72016-08-30 22:27:08 -0700772
773MidiManagerWinrt::~MidiManagerWinrt() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000774 // Unbind and take a lock to ensure that InitializeOnComRunner should not run
775 // after here.
776 bool result = service()->task_service()->UnbindInstance();
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000777 CHECK(result);
shaochuane58f9c72016-08-30 22:27:08 -0700778
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000779 base::AutoLock auto_lock(lazy_init_member_lock_);
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000780 service()->task_service()->PostStaticTask(
781 kComTaskRunner,
782 base::BindOnce(&FinalizeOnComRunner, std::move(port_manager_in_),
783 std::move(port_manager_out_)));
shaochuane58f9c72016-08-30 22:27:08 -0700784}
785
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000786void MidiManagerWinrt::StartInitialization() {
787 if (!service()->task_service()->BindInstance()) {
788 NOTREACHED();
789 CompleteInitialization(Result::INITIALIZATION_ERROR);
790 return;
791 }
792
793 service()->task_service()->PostBoundTask(
794 kComTaskRunner, base::BindOnce(&MidiManagerWinrt::InitializeOnComRunner,
795 base::Unretained(this)));
796}
797
shaochuane58f9c72016-08-30 22:27:08 -0700798void MidiManagerWinrt::DispatchSendMidiData(MidiManagerClient* client,
799 uint32_t port_index,
800 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +0000801 base::TimeTicks timestamp) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000802 base::TimeDelta delay = MidiService::TimestampToTimeDeltaDelay(timestamp);
803 service()->task_service()->PostBoundDelayedTask(
804 kComTaskRunner,
805 base::BindOnce(&MidiManagerWinrt::SendOnComRunner, base::Unretained(this),
806 port_index, data),
807 delay);
808 service()->task_service()->PostBoundDelayedTask(
809 kComTaskRunner,
810 base::BindOnce(&MidiManagerWinrt::AccumulateMidiBytesSent,
811 base::Unretained(this), client, data.size()),
812 delay);
shaochuane58f9c72016-08-30 22:27:08 -0700813}
814
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000815void MidiManagerWinrt::InitializeOnComRunner() {
shaochuane58f9c72016-08-30 22:27:08 -0700816 base::AutoLock auto_lock(lazy_init_member_lock_);
817
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000818 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
819
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000820 bool preload_success = base::win::ResolveCoreWinRTDelayload() &&
821 ScopedHString::ResolveCoreWinRTStringDelayload();
822 if (!preload_success) {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000823 service()->task_service()->PostBoundTask(
824 kDefaultTaskRunner,
825 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
826 base::Unretained(this), Result::INITIALIZATION_ERROR));
shaochuan9ff63b82016-09-01 01:58:44 -0700827 return;
828 }
829
shaochuane58f9c72016-08-30 22:27:08 -0700830 port_manager_in_.reset(new MidiInPortManager(this));
831 port_manager_out_.reset(new MidiOutPortManager(this));
832
shaochuane58f9c72016-08-30 22:27:08 -0700833 if (!(port_manager_in_->StartWatcher() &&
834 port_manager_out_->StartWatcher())) {
835 port_manager_in_->StopWatcher();
836 port_manager_out_->StopWatcher();
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000837 service()->task_service()->PostBoundTask(
838 kDefaultTaskRunner,
839 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
840 base::Unretained(this), Result::INITIALIZATION_ERROR));
shaochuane58f9c72016-08-30 22:27:08 -0700841 }
842}
843
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000844void MidiManagerWinrt::SendOnComRunner(uint32_t port_index,
shaochuane58f9c72016-08-30 22:27:08 -0700845 const std::vector<uint8_t>& data) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000846 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700847
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000848 base::AutoLock auto_lock(lazy_init_member_lock_);
shaochuane58f9c72016-08-30 22:27:08 -0700849 MidiPort<IMidiOutPort>* port = port_manager_out_->GetPortByIndex(port_index);
850 if (!(port && port->handle)) {
851 VLOG(1) << "Port not available: " << port_index;
852 return;
853 }
854
Robert Liao4a680c32017-10-18 19:10:01 +0000855 WRL::ComPtr<IBuffer> buffer;
junweifua8cea852017-10-17 06:21:16 +0000856 HRESULT hr = base::win::CreateIBufferFromData(
junweifuf51c5a02017-11-03 06:37:09 +0000857 data.data(), static_cast<UINT32>(data.size()), &buffer);
shaochuane58f9c72016-08-30 22:27:08 -0700858 if (FAILED(hr)) {
junweifua8cea852017-10-17 06:21:16 +0000859 VLOG(1) << "CreateIBufferFromData failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700860 return;
861 }
862
robliao3566d1a2017-04-18 17:28:09 -0700863 hr = port->handle->SendBuffer(buffer.Get());
shaochuane58f9c72016-08-30 22:27:08 -0700864 if (FAILED(hr)) {
865 VLOG(1) << "SendBuffer failed: " << PrintHr(hr);
866 return;
867 }
868}
869
870void MidiManagerWinrt::OnPortManagerReady() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000871 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700872 DCHECK(port_manager_ready_count_ < 2);
873
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000874 if (++port_manager_ready_count_ == 2) {
875 service()->task_service()->PostBoundTask(
876 kDefaultTaskRunner,
877 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
878 base::Unretained(this), Result::OK));
879 }
shaochuane58f9c72016-08-30 22:27:08 -0700880}
881
shaochuane58f9c72016-08-30 22:27:08 -0700882} // namespace midi