blob: 95a48401c09781a26f3fceff88153bff45c03b0e [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
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000049MidiManager::MidiManager()
shaochuan8ba1cb62016-10-24 04:34:31 -070050 : initialization_state_(InitializationState::NOT_STARTED),
51 finalized_(false),
52 result_(Result::NOT_INITIALIZED) {
toyoshim715385c2015-08-09 23:00:26 -070053 ReportUsage(Usage::CREATED);
crogers@google.com27356e42013-06-22 04:03:03 +000054}
55
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000056MidiManager::~MidiManager() {
toyoshim7a809662015-10-06 00:54:21 -070057 // Make sure that Finalize() is called to clean up resources allocated on
58 // the Chrome_IOThread.
toyoshim131beb52016-07-25 00:42:41 -070059 base::AutoLock auto_lock(lock_);
60 CHECK(finalized_);
yukawa@chromium.orgdb7ad8b2013-12-04 15:42:41 +000061}
crogers@google.com27356e42013-06-22 04:03:03 +000062
agoode7de413f2015-04-24 00:13:39 -070063#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
64 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000065MidiManager* MidiManager::Create() {
toyoshim715385c2015-08-09 23:00:26 -070066 ReportUsage(Usage::CREATED_ON_UNSUPPORTED_PLATFORMS);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000067 return new MidiManager;
68}
69#endif
70
toyoshim7a809662015-10-06 00:54:21 -070071void MidiManager::Shutdown() {
72 UMA_HISTOGRAM_ENUMERATION("Media.Midi.ResultOnShutdown",
73 static_cast<int>(result_),
74 static_cast<int>(Result::MAX) + 1);
75 base::AutoLock auto_lock(lock_);
76 if (session_thread_runner_) {
77 session_thread_runner_->PostTask(
78 FROM_HERE, base::Bind(&MidiManager::ShutdownOnSessionThread,
79 base::Unretained(this)));
80 session_thread_runner_ = nullptr;
81 } else {
82 finalized_ = true;
83 }
84}
85
toyoshima485ff92014-10-23 00:17:59 -070086void MidiManager::StartSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -070087 ReportUsage(Usage::SESSION_STARTED);
88
shaochuan8ba1cb62016-10-24 04:34:31 -070089 bool needs_initialization = false;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000090
91 {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000092 base::AutoLock auto_lock(lock_);
toyoshima485ff92014-10-23 00:17:59 -070093 if (clients_.find(client) != clients_.end() ||
94 pending_clients_.find(client) != pending_clients_.end()) {
95 // Should not happen. But just in case the renderer is compromised.
96 NOTREACHED();
97 return;
98 }
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000099
shaochuan8ba1cb62016-10-24 04:34:31 -0700100 // Do not accept a new request if Shutdown() was already called.
101 if (finalized_) {
102 client->CompleteStartSession(Result::INITIALIZATION_ERROR);
103 return;
104 }
105
106 if (initialization_state_ == InitializationState::COMPLETED) {
toyoshim7a809662015-10-06 00:54:21 -0700107 // Platform dependent initialization was already finished for previously
108 // initialized clients.
109 if (result_ == Result::OK) {
110 AddInitialPorts(client);
111 clients_.insert(client);
112 }
113 // Complete synchronously with |result_|;
shaochuan8ba1cb62016-10-24 04:34:31 -0700114 client->CompleteStartSession(result_);
ochangae3bae92016-01-12 19:42:10 -0800115 return;
116 }
shaochuan8ba1cb62016-10-24 04:34:31 -0700117
118 // Do not accept a new request if the pending client list contains too
119 // many clients.
120 if (pending_clients_.size() >= kMaxPendingClientCount) {
121 client->CompleteStartSession(Result::INITIALIZATION_ERROR);
122 return;
123 }
124
125 if (initialization_state_ == InitializationState::NOT_STARTED) {
126 // Set fields protected by |lock_| here and call StartInitialization()
127 // later.
128 needs_initialization = true;
129 session_thread_runner_ = base::ThreadTaskRunnerHandle::Get();
130 initialization_state_ = InitializationState::STARTED;
131 }
132
133 pending_clients_.insert(client);
toyoshim@chromium.orgf77a1e62014-04-11 13:16:24 +0000134 }
crogers@google.com27356e42013-06-22 04:03:03 +0000135
shaochuan8ba1cb62016-10-24 04:34:31 -0700136 if (needs_initialization) {
toyoshim7a809662015-10-06 00:54:21 -0700137 // Lazily initialize the MIDI back-end.
138 TRACE_EVENT0("midi", "MidiManager::StartInitialization");
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000139 // CompleteInitialization() will be called asynchronously when platform
140 // dependent initialization is finished.
toyoshim7a809662015-10-06 00:54:21 -0700141 StartInitialization();
crogers@google.com27356e42013-06-22 04:03:03 +0000142 }
crogers@google.com27356e42013-06-22 04:03:03 +0000143}
144
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000145void MidiManager::EndSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -0700146 ReportUsage(Usage::SESSION_ENDED);
147
toyoshima485ff92014-10-23 00:17:59 -0700148 // At this point, |client| can be in the destruction process, and calling
ochangae3bae92016-01-12 19:42:10 -0800149 // any method of |client| is dangerous. Calls on clients *must* be protected
150 // by |lock_| to prevent race conditions.
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000151 base::AutoLock auto_lock(lock_);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000152 clients_.erase(client);
153 pending_clients_.erase(client);
crogers@google.com27356e42013-06-22 04:03:03 +0000154}
155
toyoshim7dd12482015-04-07 07:02:49 -0700156void MidiManager::AccumulateMidiBytesSent(MidiManagerClient* client, size_t n) {
agoodead116b22015-12-07 00:00:35 -0800157 base::AutoLock auto_lock(lock_);
158 if (clients_.find(client) == clients_.end())
159 return;
160
agoodeb02d0962015-12-07 19:45:54 -0800161 // Continue to hold lock_ here in case another thread is currently doing
162 // EndSession.
toyoshim7dd12482015-04-07 07:02:49 -0700163 client->AccumulateMidiBytesSent(n);
164}
165
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000166void MidiManager::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500167 uint32_t port_index,
168 const std::vector<uint8_t>& data,
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000169 double timestamp) {
170 NOTREACHED();
171}
172
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000173void MidiManager::StartInitialization() {
toyoshimf1b88962015-07-09 14:14:51 -0700174 CompleteInitialization(Result::NOT_SUPPORTED);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000175}
176
toyoshimf1b88962015-07-09 14:14:51 -0700177void MidiManager::CompleteInitialization(Result result) {
toyoshim7a809662015-10-06 00:54:21 -0700178 base::AutoLock auto_lock(lock_);
179 if (session_thread_runner_) {
180 session_thread_runner_->PostTask(
181 FROM_HERE, base::Bind(&MidiManager::CompleteInitializationInternal,
182 base::Unretained(this), result));
183 }
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000184}
185
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000186void MidiManager::AddInputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700187 ReportUsage(Usage::INPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700188 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000189 input_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700190 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700191 client->AddInputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000192}
193
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000194void MidiManager::AddOutputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700195 ReportUsage(Usage::OUTPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700196 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000197 output_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700198 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700199 client->AddOutputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000200}
201
toyoshimec2570a2016-10-21 02:15:27 -0700202void MidiManager::SetInputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800203 base::AutoLock auto_lock(lock_);
204 DCHECK_LT(port_index, input_ports_.size());
205 input_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700206 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800207 client->SetInputPortState(port_index, state);
208}
209
toyoshimec2570a2016-10-21 02:15:27 -0700210void MidiManager::SetOutputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800211 base::AutoLock auto_lock(lock_);
212 DCHECK_LT(port_index, output_ports_.size());
213 output_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700214 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800215 client->SetOutputPortState(port_index, state);
216}
217
Avi Drissman3528fd02015-12-18 20:11:31 -0500218void MidiManager::ReceiveMidiData(uint32_t port_index,
219 const uint8_t* data,
220 size_t length,
221 double timestamp) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000222 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000223
vmpstr0205abb2016-06-28 18:50:56 -0700224 for (auto* client : clients_)
toyoshima485ff92014-10-23 00:17:59 -0700225 client->ReceiveMidiData(port_index, data, length, timestamp);
crogers@google.com542a43a2013-07-31 05:16:49 +0000226}
227
toyoshimf1b88962015-07-09 14:14:51 -0700228void MidiManager::CompleteInitializationInternal(Result result) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000229 TRACE_EVENT0("midi", "MidiManager::CompleteInitialization");
toyoshim715385c2015-08-09 23:00:26 -0700230 ReportUsage(Usage::INITIALIZED);
231 UMA_HISTOGRAM_ENUMERATION("Media.Midi.InputPorts",
232 static_cast<Sample>(input_ports_.size()),
233 kMaxUmaDevices + 1);
234 UMA_HISTOGRAM_ENUMERATION("Media.Midi.OutputPorts",
235 static_cast<Sample>(output_ports_.size()),
236 kMaxUmaDevices + 1);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000237
238 base::AutoLock auto_lock(lock_);
239 DCHECK(clients_.empty());
shaochuan8ba1cb62016-10-24 04:34:31 -0700240 DCHECK_EQ(initialization_state_, InitializationState::STARTED);
241 initialization_state_ = InitializationState::COMPLETED;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000242 result_ = result;
243
vmpstr0205abb2016-06-28 18:50:56 -0700244 for (auto* client : pending_clients_) {
toyoshimf1b88962015-07-09 14:14:51 -0700245 if (result_ == Result::OK) {
toyoshima62d6742014-10-23 09:05:03 -0700246 AddInitialPorts(client);
toyoshima485ff92014-10-23 00:17:59 -0700247 clients_.insert(client);
toyoshima62d6742014-10-23 09:05:03 -0700248 }
toyoshima485ff92014-10-23 00:17:59 -0700249 client->CompleteStartSession(result_);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000250 }
251 pending_clients_.clear();
252}
253
toyoshima62d6742014-10-23 09:05:03 -0700254void MidiManager::AddInitialPorts(MidiManagerClient* client) {
255 lock_.AssertAcquired();
256
257 for (const auto& info : input_ports_)
258 client->AddInputPort(info);
259 for (const auto& info : output_ports_)
260 client->AddOutputPort(info);
261}
262
toyoshim7a809662015-10-06 00:54:21 -0700263void MidiManager::ShutdownOnSessionThread() {
264 Finalize();
265 base::AutoLock auto_lock(lock_);
266 finalized_ = true;
267
268 // Detach all clients so that they do not call MidiManager methods any more.
vmpstr0205abb2016-06-28 18:50:56 -0700269 for (auto* client : clients_)
toyoshim7a809662015-10-06 00:54:21 -0700270 client->Detach();
271}
272
toyoshime147c5e2015-05-07 21:58:31 -0700273} // namespace midi