blob: 00299281200abcefe96ee67bf75accb2f6810dab [file] [log] [blame]
crogers@google.com27356e42013-06-22 04:03:03 +00001// Copyright (c) 2013 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.h"
6
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +00007#include "base/bind.h"
fdoraya05bf572016-06-09 08:42:38 -07008#include "base/location.h"
toyoshimf1b88962015-07-09 14:14:51 -07009#include "base/metrics/histogram_macros.h"
fdoraya05bf572016-06-09 08:42:38 -070010#include "base/single_thread_task_runner.h"
11#include "base/threading/thread_task_runner_handle.h"
ssid91344442015-01-28 04:13:15 -080012#include "base/trace_event/trace_event.h"
avi793390d2015-12-22 22:22:36 -080013#include "build/build_config.h"
crogers@google.com542a43a2013-07-31 05:16:49 +000014
toyoshime147c5e2015-05-07 21:58:31 -070015namespace midi {
crogers@google.com27356e42013-06-22 04:03:03 +000016
toyoshim715385c2015-08-09 23:00:26 -070017namespace {
18
19using Sample = base::HistogramBase::Sample;
toyoshimec2570a2016-10-21 02:15:27 -070020using midi::mojom::PortState;
toyoshim2f3a48f2016-10-17 01:54:13 -070021using midi::mojom::Result;
toyoshim715385c2015-08-09 23:00:26 -070022
23// If many users have more devices, this number will be increased.
24// But the number is expected to be big enough for now.
toyoshime4aa38e2017-04-09 22:59:52 -070025constexpr Sample kMaxUmaDevices = 31;
toyoshim715385c2015-08-09 23:00:26 -070026
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +090027// Used to count events for usage histogram. The item order should not be
28// changed, and new items should be just appended.
toyoshim715385c2015-08-09 23:00:26 -070029enum class Usage {
30 CREATED,
31 CREATED_ON_UNSUPPORTED_PLATFORMS,
32 SESSION_STARTED,
33 SESSION_ENDED,
34 INITIALIZED,
35 INPUT_PORT_ADDED,
36 OUTPUT_PORT_ADDED,
37
38 // New items should be inserted here, and |MAX| should point the last item.
toyoshime4aa38e2017-04-09 22:59:52 -070039 MAX = OUTPUT_PORT_ADDED,
toyoshim715385c2015-08-09 23:00:26 -070040};
41
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +090042// Used to count events for transaction usage histogram. The item order should
43// not be changed, and new items should be just appended.
44enum class SendReceiveUsage {
45 NO_USE,
46 SENT,
47 RECEIVED,
48 SENT_AND_RECEIVED,
49
50 // New items should be inserted here, and |MAX| should point the last item.
51 MAX = SENT_AND_RECEIVED,
52};
53
toyoshim715385c2015-08-09 23:00:26 -070054void ReportUsage(Usage usage) {
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +090055 UMA_HISTOGRAM_ENUMERATION("Media.Midi.Usage", usage,
toyoshim715385c2015-08-09 23:00:26 -070056 static_cast<Sample>(Usage::MAX) + 1);
57}
58
59} // namespace
60
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +000061MidiManager::MidiManager(MidiService* service) : service_(service) {
toyoshim715385c2015-08-09 23:00:26 -070062 ReportUsage(Usage::CREATED);
crogers@google.com27356e42013-06-22 04:03:03 +000063}
64
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000065MidiManager::~MidiManager() {
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090066 base::AutoLock auto_lock(lock_);
Adithya Srinivasanc262ed32018-12-26 06:20:53 +000067 DCHECK(pending_clients_.empty() && clients_.empty());
68
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +000069 if (session_thread_runner_) {
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090070 DCHECK(session_thread_runner_->BelongsToCurrentThread());
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +000071 session_thread_runner_ = nullptr;
72 }
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090073
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +090074 UMA_HISTOGRAM_ENUMERATION("Media.Midi.ResultOnShutdown", result_,
75 static_cast<Sample>(Result::MAX) + 1);
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090076
77 UMA_HISTOGRAM_ENUMERATION(
78 "Media.Midi.SendReceiveUsage",
79 data_sent_ ? (data_received_ ? SendReceiveUsage::SENT_AND_RECEIVED
80 : SendReceiveUsage::SENT)
81 : (data_received_ ? SendReceiveUsage::RECEIVED
82 : SendReceiveUsage::NO_USE),
83 static_cast<Sample>(SendReceiveUsage::MAX) + 1);
toyoshim7a809662015-10-06 00:54:21 -070084}
85
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +000086#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
87 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
88MidiManager* MidiManager::Create(MidiService* service) {
89 ReportUsage(Usage::CREATED_ON_UNSUPPORTED_PLATFORMS);
90 return new MidiManager(service);
91}
92#endif
93
toyoshima485ff92014-10-23 00:17:59 -070094void MidiManager::StartSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -070095 ReportUsage(Usage::SESSION_STARTED);
96
shaochuan8ba1cb62016-10-24 04:34:31 -070097 bool needs_initialization = false;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000098
99 {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000100 base::AutoLock auto_lock(lock_);
Takashi Toyoshima408b79f2018-01-09 20:30:07 +0900101
toyoshima485ff92014-10-23 00:17:59 -0700102 if (clients_.find(client) != clients_.end() ||
103 pending_clients_.find(client) != pending_clients_.end()) {
104 // Should not happen. But just in case the renderer is compromised.
105 NOTREACHED();
106 return;
107 }
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000108
shaochuan8ba1cb62016-10-24 04:34:31 -0700109 if (initialization_state_ == InitializationState::COMPLETED) {
toyoshim7a809662015-10-06 00:54:21 -0700110 // Platform dependent initialization was already finished for previously
111 // initialized clients.
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000112 if (result_ == Result::OK) {
113 for (const auto& info : input_ports_)
114 client->AddInputPort(info);
115 for (const auto& info : output_ports_)
116 client->AddOutputPort(info);
117 }
Takashi Toyoshimaec05edf2017-11-28 11:21:20 +0000118
toyoshim7a809662015-10-06 00:54:21 -0700119 // Complete synchronously with |result_|;
Takashi Toyoshimaec05edf2017-11-28 11:21:20 +0000120 clients_.insert(client);
shaochuan8ba1cb62016-10-24 04:34:31 -0700121 client->CompleteStartSession(result_);
ochangae3bae92016-01-12 19:42:10 -0800122 return;
123 }
shaochuan8ba1cb62016-10-24 04:34:31 -0700124
125 // Do not accept a new request if the pending client list contains too
126 // many clients.
127 if (pending_clients_.size() >= kMaxPendingClientCount) {
128 client->CompleteStartSession(Result::INITIALIZATION_ERROR);
129 return;
130 }
131
132 if (initialization_state_ == InitializationState::NOT_STARTED) {
133 // Set fields protected by |lock_| here and call StartInitialization()
134 // later.
135 needs_initialization = true;
136 session_thread_runner_ = base::ThreadTaskRunnerHandle::Get();
137 initialization_state_ = InitializationState::STARTED;
138 }
139
140 pending_clients_.insert(client);
toyoshim@chromium.orgf77a1e62014-04-11 13:16:24 +0000141 }
crogers@google.com27356e42013-06-22 04:03:03 +0000142
shaochuan8ba1cb62016-10-24 04:34:31 -0700143 if (needs_initialization) {
toyoshim7a809662015-10-06 00:54:21 -0700144 // Lazily initialize the MIDI back-end.
145 TRACE_EVENT0("midi", "MidiManager::StartInitialization");
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000146 // CompleteInitialization() will be called asynchronously when platform
147 // dependent initialization is finished.
toyoshim7a809662015-10-06 00:54:21 -0700148 StartInitialization();
crogers@google.com27356e42013-06-22 04:03:03 +0000149 }
crogers@google.com27356e42013-06-22 04:03:03 +0000150}
151
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000152bool MidiManager::EndSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -0700153 ReportUsage(Usage::SESSION_ENDED);
154
toyoshima485ff92014-10-23 00:17:59 -0700155 // At this point, |client| can be in the destruction process, and calling
ochangae3bae92016-01-12 19:42:10 -0800156 // any method of |client| is dangerous. Calls on clients *must* be protected
157 // by |lock_| to prevent race conditions.
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000158 base::AutoLock auto_lock(lock_);
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000159 if (clients_.find(client) == clients_.end() &&
160 pending_clients_.find(client) == pending_clients_.end()) {
161 return false;
162 }
163
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000164 clients_.erase(client);
165 pending_clients_.erase(client);
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000166 return true;
167}
168
169bool MidiManager::HasOpenSession() {
170 base::AutoLock auto_lock(lock_);
171 return clients_.size() != 0u;
crogers@google.com27356e42013-06-22 04:03:03 +0000172}
173
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000174void MidiManager::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500175 uint32_t port_index,
176 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +0000177 base::TimeTicks timestamp) {
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000178 NOTREACHED();
179}
180
Adithya Srinivasanc262ed32018-12-26 06:20:53 +0000181void MidiManager::EndAllSessions() {
182 base::AutoLock lock(lock_);
183 for (auto* client : pending_clients_)
184 client->Detach();
185 for (auto* client : clients_)
186 client->Detach();
187 pending_clients_.clear();
188 clients_.clear();
189}
190
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000191void MidiManager::StartInitialization() {
toyoshimf1b88962015-07-09 14:14:51 -0700192 CompleteInitialization(Result::NOT_SUPPORTED);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000193}
194
toyoshimf1b88962015-07-09 14:14:51 -0700195void MidiManager::CompleteInitialization(Result result) {
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000196 DCHECK_EQ(InitializationState::STARTED, initialization_state_);
197
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000198 TRACE_EVENT0("midi", "MidiManager::CompleteInitialization");
199 ReportUsage(Usage::INITIALIZED);
200
201 base::AutoLock auto_lock(lock_);
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000202 if (!session_thread_runner_)
203 return;
204 DCHECK(session_thread_runner_->BelongsToCurrentThread());
205
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000206 UMA_HISTOGRAM_ENUMERATION("Media.Midi.InputPorts", input_ports_.size(),
207 kMaxUmaDevices + 1);
208 UMA_HISTOGRAM_ENUMERATION("Media.Midi.OutputPorts", output_ports_.size(),
209 kMaxUmaDevices + 1);
210
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000211 DCHECK(clients_.empty());
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000212 initialization_state_ = InitializationState::COMPLETED;
213 result_ = result;
214
215 for (auto* client : pending_clients_) {
216 if (result_ == Result::OK) {
217 for (const auto& info : input_ports_)
218 client->AddInputPort(info);
219 for (const auto& info : output_ports_)
220 client->AddOutputPort(info);
toyoshimd28b59c2017-02-20 11:07:37 -0800221 }
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000222
223 clients_.insert(client);
224 client->CompleteStartSession(result_);
toyoshim7a809662015-10-06 00:54:21 -0700225 }
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000226 pending_clients_.clear();
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000227}
228
Adithya Srinivasan33252732018-10-17 15:59:40 +0000229void MidiManager::AddInputPort(const mojom::PortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700230 ReportUsage(Usage::INPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700231 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000232 input_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700233 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700234 client->AddInputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000235}
236
Adithya Srinivasan33252732018-10-17 15:59:40 +0000237void MidiManager::AddOutputPort(const mojom::PortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700238 ReportUsage(Usage::OUTPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700239 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000240 output_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700241 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700242 client->AddOutputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000243}
244
toyoshimec2570a2016-10-21 02:15:27 -0700245void MidiManager::SetInputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800246 base::AutoLock auto_lock(lock_);
247 DCHECK_LT(port_index, input_ports_.size());
248 input_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700249 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800250 client->SetInputPortState(port_index, state);
251}
252
toyoshimec2570a2016-10-21 02:15:27 -0700253void MidiManager::SetOutputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800254 base::AutoLock auto_lock(lock_);
255 DCHECK_LT(port_index, output_ports_.size());
256 output_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700257 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800258 client->SetOutputPortState(port_index, state);
259}
260
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000261mojom::PortState MidiManager::GetOutputPortState(uint32_t port_index) {
262 base::AutoLock auto_lock(lock_);
263 DCHECK_LT(port_index, output_ports_.size());
264 return output_ports_[port_index].state;
265}
266
267void MidiManager::AccumulateMidiBytesSent(MidiManagerClient* client, size_t n) {
268 base::AutoLock auto_lock(lock_);
269 data_sent_ = true;
270 if (clients_.find(client) == clients_.end())
271 return;
272
273 // Continue to hold lock_ here in case another thread is currently doing
274 // EndSession.
275 client->AccumulateMidiBytesSent(n);
276}
277
Avi Drissman3528fd02015-12-18 20:11:31 -0500278void MidiManager::ReceiveMidiData(uint32_t port_index,
279 const uint8_t* data,
280 size_t length,
tzik925e2c62018-02-02 07:39:45 +0000281 base::TimeTicks timestamp) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000282 base::AutoLock auto_lock(lock_);
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +0900283 data_received_ = true;
crogers@google.com27356e42013-06-22 04:03:03 +0000284
vmpstr0205abb2016-06-28 18:50:56 -0700285 for (auto* client : clients_)
toyoshima485ff92014-10-23 00:17:59 -0700286 client->ReceiveMidiData(port_index, data, length, timestamp);
crogers@google.com542a43a2013-07-31 05:16:49 +0000287}
288
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000289size_t MidiManager::GetClientCountForTesting() {
290 base::AutoLock auto_lock(lock_);
291 return clients_.size();
292}
293
294size_t MidiManager::GetPendingClientCountForTesting() {
Adithya Srinivasanc262ed32018-12-26 06:20:53 +0000295 base::AutoLock auto_lock(lock_);
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000296 return pending_clients_.size();
297}
298
toyoshime147c5e2015-05-07 21:58:31 -0700299} // namespace midi