blob: ad9dfcebe908560b6bfb11605a382c058238ff12 [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;
20
21// If many users have more devices, this number will be increased.
22// But the number is expected to be big enough for now.
23const Sample kMaxUmaDevices = 31;
24
25// Used to count events for usage histogram.
26enum class Usage {
27 CREATED,
28 CREATED_ON_UNSUPPORTED_PLATFORMS,
29 SESSION_STARTED,
30 SESSION_ENDED,
31 INITIALIZED,
32 INPUT_PORT_ADDED,
33 OUTPUT_PORT_ADDED,
34
35 // New items should be inserted here, and |MAX| should point the last item.
36 MAX = INITIALIZED,
37};
38
toyoshim7a809662015-10-06 00:54:21 -070039// Used in StartSession.
40enum class Completion {
41 COMPLETE_SYNCHRONOUSLY,
42 INVOKE_INITIALIZATION,
43 COMPLETE_ASYNCHRONOUSLY,
44};
45
toyoshim715385c2015-08-09 23:00:26 -070046void ReportUsage(Usage usage) {
47 UMA_HISTOGRAM_ENUMERATION("Media.Midi.Usage",
48 static_cast<Sample>(usage),
49 static_cast<Sample>(Usage::MAX) + 1);
50}
51
52} // namespace
53
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000054MidiManager::MidiManager()
toyoshim7a809662015-10-06 00:54:21 -070055 : initialized_(false), finalized_(false), result_(Result::NOT_INITIALIZED) {
toyoshim715385c2015-08-09 23:00:26 -070056 ReportUsage(Usage::CREATED);
crogers@google.com27356e42013-06-22 04:03:03 +000057}
58
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000059MidiManager::~MidiManager() {
toyoshim7a809662015-10-06 00:54:21 -070060 // Make sure that Finalize() is called to clean up resources allocated on
61 // the Chrome_IOThread.
toyoshim131beb52016-07-25 00:42:41 -070062 base::AutoLock auto_lock(lock_);
63 CHECK(finalized_);
yukawa@chromium.orgdb7ad8b2013-12-04 15:42:41 +000064}
crogers@google.com27356e42013-06-22 04:03:03 +000065
agoode7de413f2015-04-24 00:13:39 -070066#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
67 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000068MidiManager* MidiManager::Create() {
toyoshim715385c2015-08-09 23:00:26 -070069 ReportUsage(Usage::CREATED_ON_UNSUPPORTED_PLATFORMS);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000070 return new MidiManager;
71}
72#endif
73
toyoshim7a809662015-10-06 00:54:21 -070074void MidiManager::Shutdown() {
75 UMA_HISTOGRAM_ENUMERATION("Media.Midi.ResultOnShutdown",
76 static_cast<int>(result_),
77 static_cast<int>(Result::MAX) + 1);
78 base::AutoLock auto_lock(lock_);
79 if (session_thread_runner_) {
80 session_thread_runner_->PostTask(
81 FROM_HERE, base::Bind(&MidiManager::ShutdownOnSessionThread,
82 base::Unretained(this)));
83 session_thread_runner_ = nullptr;
84 } else {
85 finalized_ = true;
86 }
87}
88
toyoshima485ff92014-10-23 00:17:59 -070089void MidiManager::StartSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -070090 ReportUsage(Usage::SESSION_STARTED);
91
toyoshim7a809662015-10-06 00:54:21 -070092 Completion completion = Completion::COMPLETE_SYNCHRONOUSLY;
93 Result result = Result::NOT_INITIALIZED;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000094
95 {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000096 base::AutoLock auto_lock(lock_);
toyoshima485ff92014-10-23 00:17:59 -070097 if (clients_.find(client) != clients_.end() ||
98 pending_clients_.find(client) != pending_clients_.end()) {
99 // Should not happen. But just in case the renderer is compromised.
100 NOTREACHED();
101 return;
102 }
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000103
toyoshim7a809662015-10-06 00:54:21 -0700104 if (initialized_) {
105 // Platform dependent initialization was already finished for previously
106 // initialized clients.
107 if (result_ == Result::OK) {
108 AddInitialPorts(client);
109 clients_.insert(client);
110 }
111 // Complete synchronously with |result_|;
112 result = result_;
113 } else {
114 bool too_many_pending_clients_exist =
115 pending_clients_.size() >= kMaxPendingClientCount;
116 // Do not accept a new request if the pending client list contains too
117 // many clients, or Shutdown() was already called.
118 if (too_many_pending_clients_exist || finalized_) {
119 result = Result::INITIALIZATION_ERROR;
120 } else {
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000121 // Call StartInitialization() only for the first request.
toyoshim7a809662015-10-06 00:54:21 -0700122 if (pending_clients_.empty()) {
123 completion = Completion::INVOKE_INITIALIZATION;
fdoraya05bf572016-06-09 08:42:38 -0700124 session_thread_runner_ = base::ThreadTaskRunnerHandle::Get();
toyoshim7a809662015-10-06 00:54:21 -0700125 } else {
126 completion = Completion::COMPLETE_ASYNCHRONOUSLY;
127 }
toyoshima485ff92014-10-23 00:17:59 -0700128 pending_clients_.insert(client);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000129 }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000130 }
ochangae3bae92016-01-12 19:42:10 -0800131
132 if (completion == Completion::COMPLETE_SYNCHRONOUSLY) {
133 client->CompleteStartSession(result);
134 return;
135 }
toyoshim@chromium.orgf77a1e62014-04-11 13:16:24 +0000136 }
crogers@google.com27356e42013-06-22 04:03:03 +0000137
ochangae3bae92016-01-12 19:42:10 -0800138 if (completion == Completion::INVOKE_INITIALIZATION) {
toyoshim7a809662015-10-06 00:54:21 -0700139 // Lazily initialize the MIDI back-end.
140 TRACE_EVENT0("midi", "MidiManager::StartInitialization");
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000141 // CompleteInitialization() will be called asynchronously when platform
142 // dependent initialization is finished.
toyoshim7a809662015-10-06 00:54:21 -0700143 StartInitialization();
crogers@google.com27356e42013-06-22 04:03:03 +0000144 }
crogers@google.com27356e42013-06-22 04:03:03 +0000145}
146
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000147void MidiManager::EndSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -0700148 ReportUsage(Usage::SESSION_ENDED);
149
toyoshima485ff92014-10-23 00:17:59 -0700150 // At this point, |client| can be in the destruction process, and calling
ochangae3bae92016-01-12 19:42:10 -0800151 // any method of |client| is dangerous. Calls on clients *must* be protected
152 // by |lock_| to prevent race conditions.
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000153 base::AutoLock auto_lock(lock_);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000154 clients_.erase(client);
155 pending_clients_.erase(client);
crogers@google.com27356e42013-06-22 04:03:03 +0000156}
157
toyoshim7dd12482015-04-07 07:02:49 -0700158void MidiManager::AccumulateMidiBytesSent(MidiManagerClient* client, size_t n) {
agoodead116b22015-12-07 00:00:35 -0800159 base::AutoLock auto_lock(lock_);
160 if (clients_.find(client) == clients_.end())
161 return;
162
agoodeb02d0962015-12-07 19:45:54 -0800163 // Continue to hold lock_ here in case another thread is currently doing
164 // EndSession.
toyoshim7dd12482015-04-07 07:02:49 -0700165 client->AccumulateMidiBytesSent(n);
166}
167
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000168void MidiManager::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500169 uint32_t port_index,
170 const std::vector<uint8_t>& data,
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000171 double timestamp) {
172 NOTREACHED();
173}
174
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000175void MidiManager::StartInitialization() {
toyoshimf1b88962015-07-09 14:14:51 -0700176 CompleteInitialization(Result::NOT_SUPPORTED);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000177}
178
toyoshimf1b88962015-07-09 14:14:51 -0700179void MidiManager::CompleteInitialization(Result result) {
toyoshim7a809662015-10-06 00:54:21 -0700180 base::AutoLock auto_lock(lock_);
181 if (session_thread_runner_) {
182 session_thread_runner_->PostTask(
183 FROM_HERE, base::Bind(&MidiManager::CompleteInitializationInternal,
184 base::Unretained(this), result));
185 }
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000186}
187
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000188void MidiManager::AddInputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700189 ReportUsage(Usage::INPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700190 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000191 input_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700192 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700193 client->AddInputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000194}
195
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000196void MidiManager::AddOutputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700197 ReportUsage(Usage::OUTPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700198 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000199 output_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700200 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700201 client->AddOutputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000202}
203
Avi Drissman3528fd02015-12-18 20:11:31 -0500204void MidiManager::SetInputPortState(uint32_t port_index, MidiPortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800205 base::AutoLock auto_lock(lock_);
206 DCHECK_LT(port_index, input_ports_.size());
207 input_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700208 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800209 client->SetInputPortState(port_index, state);
210}
211
Avi Drissman3528fd02015-12-18 20:11:31 -0500212void MidiManager::SetOutputPortState(uint32_t port_index, MidiPortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800213 base::AutoLock auto_lock(lock_);
214 DCHECK_LT(port_index, output_ports_.size());
215 output_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700216 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800217 client->SetOutputPortState(port_index, state);
218}
219
Avi Drissman3528fd02015-12-18 20:11:31 -0500220void MidiManager::ReceiveMidiData(uint32_t port_index,
221 const uint8_t* data,
222 size_t length,
223 double timestamp) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000224 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000225
vmpstr0205abb2016-06-28 18:50:56 -0700226 for (auto* client : clients_)
toyoshima485ff92014-10-23 00:17:59 -0700227 client->ReceiveMidiData(port_index, data, length, timestamp);
crogers@google.com542a43a2013-07-31 05:16:49 +0000228}
229
toyoshimf1b88962015-07-09 14:14:51 -0700230void MidiManager::CompleteInitializationInternal(Result result) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000231 TRACE_EVENT0("midi", "MidiManager::CompleteInitialization");
toyoshim715385c2015-08-09 23:00:26 -0700232 ReportUsage(Usage::INITIALIZED);
233 UMA_HISTOGRAM_ENUMERATION("Media.Midi.InputPorts",
234 static_cast<Sample>(input_ports_.size()),
235 kMaxUmaDevices + 1);
236 UMA_HISTOGRAM_ENUMERATION("Media.Midi.OutputPorts",
237 static_cast<Sample>(output_ports_.size()),
238 kMaxUmaDevices + 1);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000239
240 base::AutoLock auto_lock(lock_);
241 DCHECK(clients_.empty());
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000242 DCHECK(!initialized_);
243 initialized_ = true;
244 result_ = result;
245
vmpstr0205abb2016-06-28 18:50:56 -0700246 for (auto* client : pending_clients_) {
toyoshimf1b88962015-07-09 14:14:51 -0700247 if (result_ == Result::OK) {
toyoshima62d6742014-10-23 09:05:03 -0700248 AddInitialPorts(client);
toyoshima485ff92014-10-23 00:17:59 -0700249 clients_.insert(client);
toyoshima62d6742014-10-23 09:05:03 -0700250 }
toyoshima485ff92014-10-23 00:17:59 -0700251 client->CompleteStartSession(result_);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000252 }
253 pending_clients_.clear();
254}
255
toyoshima62d6742014-10-23 09:05:03 -0700256void MidiManager::AddInitialPorts(MidiManagerClient* client) {
257 lock_.AssertAcquired();
258
259 for (const auto& info : input_ports_)
260 client->AddInputPort(info);
261 for (const auto& info : output_ports_)
262 client->AddOutputPort(info);
263}
264
toyoshim7a809662015-10-06 00:54:21 -0700265void MidiManager::ShutdownOnSessionThread() {
266 Finalize();
267 base::AutoLock auto_lock(lock_);
268 finalized_ = true;
269
270 // Detach all clients so that they do not call MidiManager methods any more.
vmpstr0205abb2016-06-28 18:50:56 -0700271 for (auto* client : clients_)
toyoshim7a809662015-10-06 00:54:21 -0700272 client->Detach();
273}
274
toyoshime147c5e2015-05-07 21:58:31 -0700275} // namespace midi