blob: 2bec2a23491e6a4e2e18cfb4961c56706629033d [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"
Patrick Monette54c53f42021-10-08 20:27:23 +000010#include "base/task/single_thread_task_runner_forward.h"
fdoraya05bf572016-06-09 08:42:38 -070011#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
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +090023// Used to count events for usage histogram. The item order should not be
24// changed, and new items should be just appended.
toyoshim715385c2015-08-09 23:00:26 -070025enum class Usage {
26 CREATED,
27 CREATED_ON_UNSUPPORTED_PLATFORMS,
28 SESSION_STARTED,
29 SESSION_ENDED,
30 INITIALIZED,
31 INPUT_PORT_ADDED,
32 OUTPUT_PORT_ADDED,
Takashi Toyoshima3d5585f2019-06-25 06:15:27 +000033 ERROR_OBSERVED,
toyoshim715385c2015-08-09 23:00:26 -070034
35 // New items should be inserted here, and |MAX| should point the last item.
Takashi Toyoshima3d5585f2019-06-25 06:15:27 +000036 MAX = ERROR_OBSERVED,
toyoshim715385c2015-08-09 23:00:26 -070037};
38
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +090039// Used to count events for transaction usage histogram. The item order should
40// not be changed, and new items should be just appended.
41enum class SendReceiveUsage {
42 NO_USE,
43 SENT,
44 RECEIVED,
45 SENT_AND_RECEIVED,
46
47 // New items should be inserted here, and |MAX| should point the last item.
48 MAX = SENT_AND_RECEIVED,
49};
50
toyoshim715385c2015-08-09 23:00:26 -070051void ReportUsage(Usage usage) {
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +090052 UMA_HISTOGRAM_ENUMERATION("Media.Midi.Usage", usage,
toyoshim715385c2015-08-09 23:00:26 -070053 static_cast<Sample>(Usage::MAX) + 1);
54}
55
56} // namespace
57
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +000058MidiManager::MidiManager(MidiService* service) : service_(service) {
toyoshim715385c2015-08-09 23:00:26 -070059 ReportUsage(Usage::CREATED);
crogers@google.com27356e42013-06-22 04:03:03 +000060}
61
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000062MidiManager::~MidiManager() {
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090063 base::AutoLock auto_lock(lock_);
Adithya Srinivasanc262ed32018-12-26 06:20:53 +000064 DCHECK(pending_clients_.empty() && clients_.empty());
65
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +000066 if (session_thread_runner_) {
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090067 DCHECK(session_thread_runner_->BelongsToCurrentThread());
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +000068 session_thread_runner_ = nullptr;
69 }
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090070
Takashi Toyoshima3d5585f2019-06-25 06:15:27 +000071 if (result_ == Result::INITIALIZATION_ERROR)
72 ReportUsage(Usage::ERROR_OBSERVED);
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090073
74 UMA_HISTOGRAM_ENUMERATION(
75 "Media.Midi.SendReceiveUsage",
76 data_sent_ ? (data_received_ ? SendReceiveUsage::SENT_AND_RECEIVED
77 : SendReceiveUsage::SENT)
78 : (data_received_ ? SendReceiveUsage::RECEIVED
79 : SendReceiveUsage::NO_USE),
80 static_cast<Sample>(SendReceiveUsage::MAX) + 1);
toyoshim7a809662015-10-06 00:54:21 -070081}
82
Avi Drissman56d38b62020-07-29 19:29:27 +000083#if !defined(OS_MAC) && !defined(OS_WIN) && \
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +000084 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
85MidiManager* MidiManager::Create(MidiService* service) {
86 ReportUsage(Usage::CREATED_ON_UNSUPPORTED_PLATFORMS);
87 return new MidiManager(service);
88}
89#endif
90
toyoshima485ff92014-10-23 00:17:59 -070091void MidiManager::StartSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -070092 ReportUsage(Usage::SESSION_STARTED);
93
shaochuan8ba1cb62016-10-24 04:34:31 -070094 bool needs_initialization = false;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000095
96 {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000097 base::AutoLock auto_lock(lock_);
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090098
toyoshima485ff92014-10-23 00:17:59 -070099 if (clients_.find(client) != clients_.end() ||
100 pending_clients_.find(client) != pending_clients_.end()) {
101 // Should not happen. But just in case the renderer is compromised.
102 NOTREACHED();
103 return;
104 }
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000105
shaochuan8ba1cb62016-10-24 04:34:31 -0700106 if (initialization_state_ == InitializationState::COMPLETED) {
toyoshim7a809662015-10-06 00:54:21 -0700107 // Platform dependent initialization was already finished for previously
108 // initialized clients.
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000109 if (result_ == Result::OK) {
110 for (const auto& info : input_ports_)
111 client->AddInputPort(info);
112 for (const auto& info : output_ports_)
113 client->AddOutputPort(info);
114 }
Takashi Toyoshimaec05edf2017-11-28 11:21:20 +0000115
toyoshim7a809662015-10-06 00:54:21 -0700116 // Complete synchronously with |result_|;
Takashi Toyoshimaec05edf2017-11-28 11:21:20 +0000117 clients_.insert(client);
shaochuan8ba1cb62016-10-24 04:34:31 -0700118 client->CompleteStartSession(result_);
ochangae3bae92016-01-12 19:42:10 -0800119 return;
120 }
shaochuan8ba1cb62016-10-24 04:34:31 -0700121
122 // Do not accept a new request if the pending client list contains too
123 // many clients.
124 if (pending_clients_.size() >= kMaxPendingClientCount) {
125 client->CompleteStartSession(Result::INITIALIZATION_ERROR);
126 return;
127 }
128
129 if (initialization_state_ == InitializationState::NOT_STARTED) {
130 // Set fields protected by |lock_| here and call StartInitialization()
131 // later.
132 needs_initialization = true;
133 session_thread_runner_ = base::ThreadTaskRunnerHandle::Get();
134 initialization_state_ = InitializationState::STARTED;
135 }
136
137 pending_clients_.insert(client);
toyoshim@chromium.orgf77a1e62014-04-11 13:16:24 +0000138 }
crogers@google.com27356e42013-06-22 04:03:03 +0000139
shaochuan8ba1cb62016-10-24 04:34:31 -0700140 if (needs_initialization) {
toyoshim7a809662015-10-06 00:54:21 -0700141 // Lazily initialize the MIDI back-end.
142 TRACE_EVENT0("midi", "MidiManager::StartInitialization");
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000143 // CompleteInitialization() will be called asynchronously when platform
144 // dependent initialization is finished.
toyoshim7a809662015-10-06 00:54:21 -0700145 StartInitialization();
crogers@google.com27356e42013-06-22 04:03:03 +0000146 }
crogers@google.com27356e42013-06-22 04:03:03 +0000147}
148
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000149bool MidiManager::EndSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -0700150 ReportUsage(Usage::SESSION_ENDED);
151
toyoshima485ff92014-10-23 00:17:59 -0700152 // At this point, |client| can be in the destruction process, and calling
ochangae3bae92016-01-12 19:42:10 -0800153 // any method of |client| is dangerous. Calls on clients *must* be protected
154 // by |lock_| to prevent race conditions.
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000155 base::AutoLock auto_lock(lock_);
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000156 if (clients_.find(client) == clients_.end() &&
157 pending_clients_.find(client) == pending_clients_.end()) {
158 return false;
159 }
160
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000161 clients_.erase(client);
162 pending_clients_.erase(client);
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000163 return true;
164}
165
166bool MidiManager::HasOpenSession() {
167 base::AutoLock auto_lock(lock_);
168 return clients_.size() != 0u;
crogers@google.com27356e42013-06-22 04:03:03 +0000169}
170
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000171void MidiManager::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500172 uint32_t port_index,
173 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +0000174 base::TimeTicks timestamp) {
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000175 NOTREACHED();
176}
177
Adithya Srinivasanc262ed32018-12-26 06:20:53 +0000178void MidiManager::EndAllSessions() {
179 base::AutoLock lock(lock_);
180 for (auto* client : pending_clients_)
181 client->Detach();
182 for (auto* client : clients_)
183 client->Detach();
184 pending_clients_.clear();
185 clients_.clear();
186}
187
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000188void MidiManager::StartInitialization() {
toyoshimf1b88962015-07-09 14:14:51 -0700189 CompleteInitialization(Result::NOT_SUPPORTED);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000190}
191
toyoshimf1b88962015-07-09 14:14:51 -0700192void MidiManager::CompleteInitialization(Result result) {
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000193 DCHECK_EQ(InitializationState::STARTED, initialization_state_);
194
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000195 TRACE_EVENT0("midi", "MidiManager::CompleteInitialization");
196 ReportUsage(Usage::INITIALIZED);
197
198 base::AutoLock auto_lock(lock_);
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000199 if (!session_thread_runner_)
200 return;
201 DCHECK(session_thread_runner_->BelongsToCurrentThread());
202
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000203 DCHECK(clients_.empty());
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000204 initialization_state_ = InitializationState::COMPLETED;
205 result_ = result;
206
207 for (auto* client : pending_clients_) {
208 if (result_ == Result::OK) {
209 for (const auto& info : input_ports_)
210 client->AddInputPort(info);
211 for (const auto& info : output_ports_)
212 client->AddOutputPort(info);
toyoshimd28b59c2017-02-20 11:07:37 -0800213 }
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000214
215 clients_.insert(client);
216 client->CompleteStartSession(result_);
toyoshim7a809662015-10-06 00:54:21 -0700217 }
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000218 pending_clients_.clear();
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000219}
220
Adithya Srinivasan33252732018-10-17 15:59:40 +0000221void MidiManager::AddInputPort(const mojom::PortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700222 ReportUsage(Usage::INPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700223 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000224 input_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700225 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700226 client->AddInputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000227}
228
Adithya Srinivasan33252732018-10-17 15:59:40 +0000229void MidiManager::AddOutputPort(const mojom::PortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700230 ReportUsage(Usage::OUTPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700231 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000232 output_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700233 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700234 client->AddOutputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000235}
236
toyoshimec2570a2016-10-21 02:15:27 -0700237void MidiManager::SetInputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800238 base::AutoLock auto_lock(lock_);
239 DCHECK_LT(port_index, input_ports_.size());
240 input_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700241 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800242 client->SetInputPortState(port_index, state);
243}
244
toyoshimec2570a2016-10-21 02:15:27 -0700245void MidiManager::SetOutputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800246 base::AutoLock auto_lock(lock_);
247 DCHECK_LT(port_index, output_ports_.size());
248 output_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700249 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800250 client->SetOutputPortState(port_index, state);
251}
252
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000253mojom::PortState MidiManager::GetOutputPortState(uint32_t port_index) {
254 base::AutoLock auto_lock(lock_);
255 DCHECK_LT(port_index, output_ports_.size());
256 return output_ports_[port_index].state;
257}
258
259void MidiManager::AccumulateMidiBytesSent(MidiManagerClient* client, size_t n) {
260 base::AutoLock auto_lock(lock_);
261 data_sent_ = true;
262 if (clients_.find(client) == clients_.end())
263 return;
264
265 // Continue to hold lock_ here in case another thread is currently doing
266 // EndSession.
267 client->AccumulateMidiBytesSent(n);
268}
269
Avi Drissman3528fd02015-12-18 20:11:31 -0500270void MidiManager::ReceiveMidiData(uint32_t port_index,
271 const uint8_t* data,
272 size_t length,
tzik925e2c62018-02-02 07:39:45 +0000273 base::TimeTicks timestamp) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000274 base::AutoLock auto_lock(lock_);
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +0900275 data_received_ = true;
crogers@google.com27356e42013-06-22 04:03:03 +0000276
vmpstr0205abb2016-06-28 18:50:56 -0700277 for (auto* client : clients_)
toyoshima485ff92014-10-23 00:17:59 -0700278 client->ReceiveMidiData(port_index, data, length, timestamp);
crogers@google.com542a43a2013-07-31 05:16:49 +0000279}
280
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000281size_t MidiManager::GetClientCountForTesting() {
282 base::AutoLock auto_lock(lock_);
283 return clients_.size();
284}
285
286size_t MidiManager::GetPendingClientCountForTesting() {
Adithya Srinivasanc262ed32018-12-26 06:20:53 +0000287 base::AutoLock auto_lock(lock_);
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000288 return pending_clients_.size();
289}
290
toyoshime147c5e2015-05-07 21:58:31 -0700291} // namespace midi