blob: 420163547162d9ea0ea53de3bdf59e098e762772 [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.
25const Sample kMaxUmaDevices = 31;
26
27// Used to count events for usage histogram.
28enum class Usage {
29 CREATED,
30 CREATED_ON_UNSUPPORTED_PLATFORMS,
31 SESSION_STARTED,
32 SESSION_ENDED,
33 INITIALIZED,
34 INPUT_PORT_ADDED,
35 OUTPUT_PORT_ADDED,
36
37 // New items should be inserted here, and |MAX| should point the last item.
38 MAX = INITIALIZED,
39};
40
41void ReportUsage(Usage usage) {
42 UMA_HISTOGRAM_ENUMERATION("Media.Midi.Usage",
43 static_cast<Sample>(usage),
44 static_cast<Sample>(Usage::MAX) + 1);
45}
46
47} // namespace
48
toyoshimf4d61522017-02-10 02:03:32 -080049MidiManager::MidiManager(MidiService* service)
shaochuan8ba1cb62016-10-24 04:34:31 -070050 : initialization_state_(InitializationState::NOT_STARTED),
51 finalized_(false),
toyoshimf4d61522017-02-10 02:03:32 -080052 result_(Result::NOT_INITIALIZED),
53 service_(service) {
toyoshim715385c2015-08-09 23:00:26 -070054 ReportUsage(Usage::CREATED);
crogers@google.com27356e42013-06-22 04:03:03 +000055}
56
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000057MidiManager::~MidiManager() {
toyoshim7a809662015-10-06 00:54:21 -070058 // Make sure that Finalize() is called to clean up resources allocated on
59 // the Chrome_IOThread.
toyoshim131beb52016-07-25 00:42:41 -070060 base::AutoLock auto_lock(lock_);
61 CHECK(finalized_);
yukawa@chromium.orgdb7ad8b2013-12-04 15:42:41 +000062}
crogers@google.com27356e42013-06-22 04:03:03 +000063
agoode7de413f2015-04-24 00:13:39 -070064#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
65 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
toyoshimf4d61522017-02-10 02:03:32 -080066MidiManager* MidiManager::Create(MidiService* service) {
toyoshim715385c2015-08-09 23:00:26 -070067 ReportUsage(Usage::CREATED_ON_UNSUPPORTED_PLATFORMS);
toyoshimf4d61522017-02-10 02:03:32 -080068 return new MidiManager(service);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000069}
70#endif
71
toyoshim7a809662015-10-06 00:54:21 -070072void MidiManager::Shutdown() {
73 UMA_HISTOGRAM_ENUMERATION("Media.Midi.ResultOnShutdown",
74 static_cast<int>(result_),
75 static_cast<int>(Result::MAX) + 1);
toyoshimf4d61522017-02-10 02:03:32 -080076 bool shutdown_synchronously = false;
77 {
78 base::AutoLock auto_lock(lock_);
79 if (session_thread_runner_) {
80 if (session_thread_runner_->BelongsToCurrentThread()) {
81 shutdown_synchronously = true;
82 } else {
83 session_thread_runner_->PostTask(
84 FROM_HERE, base::Bind(&MidiManager::ShutdownOnSessionThread,
85 base::Unretained(this)));
86 }
87 session_thread_runner_ = nullptr;
88 } else {
89 finalized_ = true;
90 }
toyoshim7a809662015-10-06 00:54:21 -070091 }
toyoshimf4d61522017-02-10 02:03:32 -080092 if (shutdown_synchronously)
93 ShutdownOnSessionThread();
toyoshim7a809662015-10-06 00:54:21 -070094}
95
toyoshima485ff92014-10-23 00:17:59 -070096void MidiManager::StartSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -070097 ReportUsage(Usage::SESSION_STARTED);
98
shaochuan8ba1cb62016-10-24 04:34:31 -070099 bool needs_initialization = false;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000100
101 {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000102 base::AutoLock auto_lock(lock_);
toyoshima485ff92014-10-23 00:17:59 -0700103 if (clients_.find(client) != clients_.end() ||
104 pending_clients_.find(client) != pending_clients_.end()) {
105 // Should not happen. But just in case the renderer is compromised.
106 NOTREACHED();
107 return;
108 }
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000109
shaochuan8ba1cb62016-10-24 04:34:31 -0700110 // Do not accept a new request if Shutdown() was already called.
111 if (finalized_) {
112 client->CompleteStartSession(Result::INITIALIZATION_ERROR);
113 return;
114 }
115
116 if (initialization_state_ == InitializationState::COMPLETED) {
toyoshim7a809662015-10-06 00:54:21 -0700117 // Platform dependent initialization was already finished for previously
118 // initialized clients.
119 if (result_ == Result::OK) {
120 AddInitialPorts(client);
121 clients_.insert(client);
122 }
123 // Complete synchronously with |result_|;
shaochuan8ba1cb62016-10-24 04:34:31 -0700124 client->CompleteStartSession(result_);
ochangae3bae92016-01-12 19:42:10 -0800125 return;
126 }
shaochuan8ba1cb62016-10-24 04:34:31 -0700127
128 // Do not accept a new request if the pending client list contains too
129 // many clients.
130 if (pending_clients_.size() >= kMaxPendingClientCount) {
131 client->CompleteStartSession(Result::INITIALIZATION_ERROR);
132 return;
133 }
134
135 if (initialization_state_ == InitializationState::NOT_STARTED) {
136 // Set fields protected by |lock_| here and call StartInitialization()
137 // later.
138 needs_initialization = true;
139 session_thread_runner_ = base::ThreadTaskRunnerHandle::Get();
140 initialization_state_ = InitializationState::STARTED;
141 }
142
143 pending_clients_.insert(client);
toyoshim@chromium.orgf77a1e62014-04-11 13:16:24 +0000144 }
crogers@google.com27356e42013-06-22 04:03:03 +0000145
shaochuan8ba1cb62016-10-24 04:34:31 -0700146 if (needs_initialization) {
toyoshim7a809662015-10-06 00:54:21 -0700147 // Lazily initialize the MIDI back-end.
148 TRACE_EVENT0("midi", "MidiManager::StartInitialization");
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000149 // CompleteInitialization() will be called asynchronously when platform
150 // dependent initialization is finished.
toyoshim7a809662015-10-06 00:54:21 -0700151 StartInitialization();
crogers@google.com27356e42013-06-22 04:03:03 +0000152 }
crogers@google.com27356e42013-06-22 04:03:03 +0000153}
154
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000155void MidiManager::EndSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -0700156 ReportUsage(Usage::SESSION_ENDED);
157
toyoshima485ff92014-10-23 00:17:59 -0700158 // At this point, |client| can be in the destruction process, and calling
ochangae3bae92016-01-12 19:42:10 -0800159 // any method of |client| is dangerous. Calls on clients *must* be protected
160 // by |lock_| to prevent race conditions.
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000161 base::AutoLock auto_lock(lock_);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000162 clients_.erase(client);
163 pending_clients_.erase(client);
crogers@google.com27356e42013-06-22 04:03:03 +0000164}
165
toyoshim7dd12482015-04-07 07:02:49 -0700166void MidiManager::AccumulateMidiBytesSent(MidiManagerClient* client, size_t n) {
agoodead116b22015-12-07 00:00:35 -0800167 base::AutoLock auto_lock(lock_);
168 if (clients_.find(client) == clients_.end())
169 return;
170
agoodeb02d0962015-12-07 19:45:54 -0800171 // Continue to hold lock_ here in case another thread is currently doing
172 // EndSession.
toyoshim7dd12482015-04-07 07:02:49 -0700173 client->AccumulateMidiBytesSent(n);
174}
175
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000176void MidiManager::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500177 uint32_t port_index,
178 const std::vector<uint8_t>& data,
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000179 double timestamp) {
180 NOTREACHED();
181}
182
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000183void MidiManager::StartInitialization() {
toyoshimf1b88962015-07-09 14:14:51 -0700184 CompleteInitialization(Result::NOT_SUPPORTED);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000185}
186
toyoshimf1b88962015-07-09 14:14:51 -0700187void MidiManager::CompleteInitialization(Result result) {
toyoshim7a809662015-10-06 00:54:21 -0700188 base::AutoLock auto_lock(lock_);
189 if (session_thread_runner_) {
190 session_thread_runner_->PostTask(
191 FROM_HERE, base::Bind(&MidiManager::CompleteInitializationInternal,
192 base::Unretained(this), result));
193 }
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000194}
195
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000196void MidiManager::AddInputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700197 ReportUsage(Usage::INPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700198 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000199 input_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700200 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700201 client->AddInputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000202}
203
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000204void MidiManager::AddOutputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700205 ReportUsage(Usage::OUTPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700206 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000207 output_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700208 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700209 client->AddOutputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000210}
211
toyoshimec2570a2016-10-21 02:15:27 -0700212void MidiManager::SetInputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800213 base::AutoLock auto_lock(lock_);
214 DCHECK_LT(port_index, input_ports_.size());
215 input_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700216 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800217 client->SetInputPortState(port_index, state);
218}
219
toyoshimec2570a2016-10-21 02:15:27 -0700220void MidiManager::SetOutputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800221 base::AutoLock auto_lock(lock_);
222 DCHECK_LT(port_index, output_ports_.size());
223 output_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700224 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800225 client->SetOutputPortState(port_index, state);
226}
227
Avi Drissman3528fd02015-12-18 20:11:31 -0500228void MidiManager::ReceiveMidiData(uint32_t port_index,
229 const uint8_t* data,
230 size_t length,
231 double timestamp) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000232 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000233
vmpstr0205abb2016-06-28 18:50:56 -0700234 for (auto* client : clients_)
toyoshima485ff92014-10-23 00:17:59 -0700235 client->ReceiveMidiData(port_index, data, length, timestamp);
crogers@google.com542a43a2013-07-31 05:16:49 +0000236}
237
toyoshimf1b88962015-07-09 14:14:51 -0700238void MidiManager::CompleteInitializationInternal(Result result) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000239 TRACE_EVENT0("midi", "MidiManager::CompleteInitialization");
toyoshim715385c2015-08-09 23:00:26 -0700240 ReportUsage(Usage::INITIALIZED);
241 UMA_HISTOGRAM_ENUMERATION("Media.Midi.InputPorts",
242 static_cast<Sample>(input_ports_.size()),
243 kMaxUmaDevices + 1);
244 UMA_HISTOGRAM_ENUMERATION("Media.Midi.OutputPorts",
245 static_cast<Sample>(output_ports_.size()),
246 kMaxUmaDevices + 1);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000247
248 base::AutoLock auto_lock(lock_);
249 DCHECK(clients_.empty());
shaochuan8ba1cb62016-10-24 04:34:31 -0700250 DCHECK_EQ(initialization_state_, InitializationState::STARTED);
251 initialization_state_ = InitializationState::COMPLETED;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000252 result_ = result;
253
vmpstr0205abb2016-06-28 18:50:56 -0700254 for (auto* client : pending_clients_) {
toyoshimf1b88962015-07-09 14:14:51 -0700255 if (result_ == Result::OK) {
toyoshima62d6742014-10-23 09:05:03 -0700256 AddInitialPorts(client);
toyoshima485ff92014-10-23 00:17:59 -0700257 clients_.insert(client);
toyoshima62d6742014-10-23 09:05:03 -0700258 }
toyoshima485ff92014-10-23 00:17:59 -0700259 client->CompleteStartSession(result_);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000260 }
261 pending_clients_.clear();
262}
263
toyoshima62d6742014-10-23 09:05:03 -0700264void MidiManager::AddInitialPorts(MidiManagerClient* client) {
265 lock_.AssertAcquired();
266
267 for (const auto& info : input_ports_)
268 client->AddInputPort(info);
269 for (const auto& info : output_ports_)
270 client->AddOutputPort(info);
271}
272
toyoshim7a809662015-10-06 00:54:21 -0700273void MidiManager::ShutdownOnSessionThread() {
274 Finalize();
275 base::AutoLock auto_lock(lock_);
276 finalized_ = true;
277
278 // Detach all clients so that they do not call MidiManager methods any more.
vmpstr0205abb2016-06-28 18:50:56 -0700279 for (auto* client : clients_)
toyoshim7a809662015-10-06 00:54:21 -0700280 client->Detach();
281}
282
toyoshime147c5e2015-05-07 21:58:31 -0700283} // namespace midi