blob: 913faca54ba7d64daed3cbf6fcfdb98e4ab3188d [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() {
toyoshim131beb52016-07-25 00:42:41 -070066 CHECK(finalized_);
yukawa@chromium.orgdb7ad8b2013-12-04 15:42:41 +000067}
crogers@google.com27356e42013-06-22 04:03:03 +000068
agoode7de413f2015-04-24 00:13:39 -070069#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
70 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
toyoshimf4d61522017-02-10 02:03:32 -080071MidiManager* MidiManager::Create(MidiService* service) {
toyoshim715385c2015-08-09 23:00:26 -070072 ReportUsage(Usage::CREATED_ON_UNSUPPORTED_PLATFORMS);
toyoshimf4d61522017-02-10 02:03:32 -080073 return new MidiManager(service);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000074}
75#endif
76
toyoshim7a809662015-10-06 00:54:21 -070077void MidiManager::Shutdown() {
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090078 Finalize();
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +000079 finalized_ = true;
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090080
81 base::AutoLock auto_lock(lock_);
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +000082 if (session_thread_runner_) {
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090083 DCHECK(session_thread_runner_->BelongsToCurrentThread());
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +000084 session_thread_runner_ = nullptr;
85 }
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090086
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +090087 UMA_HISTOGRAM_ENUMERATION("Media.Midi.ResultOnShutdown", result_,
88 static_cast<Sample>(Result::MAX) + 1);
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090089
90 UMA_HISTOGRAM_ENUMERATION(
91 "Media.Midi.SendReceiveUsage",
92 data_sent_ ? (data_received_ ? SendReceiveUsage::SENT_AND_RECEIVED
93 : SendReceiveUsage::SENT)
94 : (data_received_ ? SendReceiveUsage::RECEIVED
95 : SendReceiveUsage::NO_USE),
96 static_cast<Sample>(SendReceiveUsage::MAX) + 1);
97
98 // Detach all clients so that they do not call MidiManager methods any more.
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +000099 for (auto* client : pending_clients_)
100 client->Detach();
101 pending_clients_.clear();
Takashi Toyoshima408b79f2018-01-09 20:30:07 +0900102 for (auto* client : clients_)
103 client->Detach();
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000104 clients_.clear();
toyoshim7a809662015-10-06 00:54:21 -0700105}
106
toyoshima485ff92014-10-23 00:17:59 -0700107void MidiManager::StartSession(MidiManagerClient* client) {
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000108 DCHECK(!finalized_);
toyoshim715385c2015-08-09 23:00:26 -0700109 ReportUsage(Usage::SESSION_STARTED);
110
shaochuan8ba1cb62016-10-24 04:34:31 -0700111 bool needs_initialization = false;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000112
113 {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000114 base::AutoLock auto_lock(lock_);
Takashi Toyoshima408b79f2018-01-09 20:30:07 +0900115
toyoshima485ff92014-10-23 00:17:59 -0700116 if (clients_.find(client) != clients_.end() ||
117 pending_clients_.find(client) != pending_clients_.end()) {
118 // Should not happen. But just in case the renderer is compromised.
119 NOTREACHED();
120 return;
121 }
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000122
shaochuan8ba1cb62016-10-24 04:34:31 -0700123 if (initialization_state_ == InitializationState::COMPLETED) {
toyoshim7a809662015-10-06 00:54:21 -0700124 // Platform dependent initialization was already finished for previously
125 // initialized clients.
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000126 if (result_ == Result::OK) {
127 for (const auto& info : input_ports_)
128 client->AddInputPort(info);
129 for (const auto& info : output_ports_)
130 client->AddOutputPort(info);
131 }
Takashi Toyoshimaec05edf2017-11-28 11:21:20 +0000132
toyoshim7a809662015-10-06 00:54:21 -0700133 // Complete synchronously with |result_|;
Takashi Toyoshimaec05edf2017-11-28 11:21:20 +0000134 clients_.insert(client);
shaochuan8ba1cb62016-10-24 04:34:31 -0700135 client->CompleteStartSession(result_);
ochangae3bae92016-01-12 19:42:10 -0800136 return;
137 }
shaochuan8ba1cb62016-10-24 04:34:31 -0700138
139 // Do not accept a new request if the pending client list contains too
140 // many clients.
141 if (pending_clients_.size() >= kMaxPendingClientCount) {
142 client->CompleteStartSession(Result::INITIALIZATION_ERROR);
143 return;
144 }
145
146 if (initialization_state_ == InitializationState::NOT_STARTED) {
147 // Set fields protected by |lock_| here and call StartInitialization()
148 // later.
149 needs_initialization = true;
150 session_thread_runner_ = base::ThreadTaskRunnerHandle::Get();
151 initialization_state_ = InitializationState::STARTED;
152 }
153
154 pending_clients_.insert(client);
toyoshim@chromium.orgf77a1e62014-04-11 13:16:24 +0000155 }
crogers@google.com27356e42013-06-22 04:03:03 +0000156
shaochuan8ba1cb62016-10-24 04:34:31 -0700157 if (needs_initialization) {
toyoshim7a809662015-10-06 00:54:21 -0700158 // Lazily initialize the MIDI back-end.
159 TRACE_EVENT0("midi", "MidiManager::StartInitialization");
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000160 // CompleteInitialization() will be called asynchronously when platform
161 // dependent initialization is finished.
toyoshim7a809662015-10-06 00:54:21 -0700162 StartInitialization();
crogers@google.com27356e42013-06-22 04:03:03 +0000163 }
crogers@google.com27356e42013-06-22 04:03:03 +0000164}
165
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000166bool MidiManager::EndSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -0700167 ReportUsage(Usage::SESSION_ENDED);
168
toyoshima485ff92014-10-23 00:17:59 -0700169 // At this point, |client| can be in the destruction process, and calling
ochangae3bae92016-01-12 19:42:10 -0800170 // any method of |client| is dangerous. Calls on clients *must* be protected
171 // by |lock_| to prevent race conditions.
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000172 base::AutoLock auto_lock(lock_);
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000173 if (clients_.find(client) == clients_.end() &&
174 pending_clients_.find(client) == pending_clients_.end()) {
175 return false;
176 }
177
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000178 clients_.erase(client);
179 pending_clients_.erase(client);
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000180 return true;
181}
182
183bool MidiManager::HasOpenSession() {
184 base::AutoLock auto_lock(lock_);
185 return clients_.size() != 0u;
crogers@google.com27356e42013-06-22 04:03:03 +0000186}
187
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000188void MidiManager::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500189 uint32_t port_index,
190 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +0000191 base::TimeTicks timestamp) {
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000192 NOTREACHED();
193}
194
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000195void MidiManager::StartInitialization() {
toyoshimf1b88962015-07-09 14:14:51 -0700196 CompleteInitialization(Result::NOT_SUPPORTED);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000197}
198
toyoshimf1b88962015-07-09 14:14:51 -0700199void MidiManager::CompleteInitialization(Result result) {
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000200 DCHECK(!finalized_);
201 DCHECK_EQ(InitializationState::STARTED, initialization_state_);
202
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000203 TRACE_EVENT0("midi", "MidiManager::CompleteInitialization");
204 ReportUsage(Usage::INITIALIZED);
205
206 base::AutoLock auto_lock(lock_);
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000207 if (!session_thread_runner_)
208 return;
209 DCHECK(session_thread_runner_->BelongsToCurrentThread());
210
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000211 UMA_HISTOGRAM_ENUMERATION("Media.Midi.InputPorts", input_ports_.size(),
212 kMaxUmaDevices + 1);
213 UMA_HISTOGRAM_ENUMERATION("Media.Midi.OutputPorts", output_ports_.size(),
214 kMaxUmaDevices + 1);
215
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000216 DCHECK(clients_.empty());
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000217 initialization_state_ = InitializationState::COMPLETED;
218 result_ = result;
219
220 for (auto* client : pending_clients_) {
221 if (result_ == Result::OK) {
222 for (const auto& info : input_ports_)
223 client->AddInputPort(info);
224 for (const auto& info : output_ports_)
225 client->AddOutputPort(info);
toyoshimd28b59c2017-02-20 11:07:37 -0800226 }
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000227
228 clients_.insert(client);
229 client->CompleteStartSession(result_);
toyoshim7a809662015-10-06 00:54:21 -0700230 }
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000231 pending_clients_.clear();
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000232}
233
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000234void MidiManager::AddInputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700235 ReportUsage(Usage::INPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700236 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000237 input_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700238 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700239 client->AddInputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000240}
241
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000242void MidiManager::AddOutputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700243 ReportUsage(Usage::OUTPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700244 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000245 output_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700246 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700247 client->AddOutputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000248}
249
toyoshimec2570a2016-10-21 02:15:27 -0700250void MidiManager::SetInputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800251 base::AutoLock auto_lock(lock_);
252 DCHECK_LT(port_index, input_ports_.size());
253 input_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700254 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800255 client->SetInputPortState(port_index, state);
256}
257
toyoshimec2570a2016-10-21 02:15:27 -0700258void MidiManager::SetOutputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800259 base::AutoLock auto_lock(lock_);
260 DCHECK_LT(port_index, output_ports_.size());
261 output_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700262 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800263 client->SetOutputPortState(port_index, state);
264}
265
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000266mojom::PortState MidiManager::GetOutputPortState(uint32_t port_index) {
267 base::AutoLock auto_lock(lock_);
268 DCHECK_LT(port_index, output_ports_.size());
269 return output_ports_[port_index].state;
270}
271
272void MidiManager::AccumulateMidiBytesSent(MidiManagerClient* client, size_t n) {
273 base::AutoLock auto_lock(lock_);
274 data_sent_ = true;
275 if (clients_.find(client) == clients_.end())
276 return;
277
278 // Continue to hold lock_ here in case another thread is currently doing
279 // EndSession.
280 client->AccumulateMidiBytesSent(n);
281}
282
Avi Drissman3528fd02015-12-18 20:11:31 -0500283void MidiManager::ReceiveMidiData(uint32_t port_index,
284 const uint8_t* data,
285 size_t length,
tzik925e2c62018-02-02 07:39:45 +0000286 base::TimeTicks timestamp) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000287 base::AutoLock auto_lock(lock_);
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +0900288 data_received_ = true;
crogers@google.com27356e42013-06-22 04:03:03 +0000289
vmpstr0205abb2016-06-28 18:50:56 -0700290 for (auto* client : clients_)
toyoshima485ff92014-10-23 00:17:59 -0700291 client->ReceiveMidiData(port_index, data, length, timestamp);
crogers@google.com542a43a2013-07-31 05:16:49 +0000292}
293
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000294size_t MidiManager::GetClientCountForTesting() {
295 base::AutoLock auto_lock(lock_);
296 return clients_.size();
297}
298
299size_t MidiManager::GetPendingClientCountForTesting() {
300 return pending_clients_.size();
301}
302
toyoshime147c5e2015-05-07 21:58:31 -0700303} // namespace midi