blob: fcbaa55a03b027b4a86809336909f18ddf0d7590 [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
crogers@google.com27356e42013-06-22 04:03:03 +000015namespace media {
toyoshime147c5e2015-05-07 21:58:31 -070016namespace midi {
crogers@google.com27356e42013-06-22 04:03:03 +000017
toyoshim715385c2015-08-09 23:00:26 -070018namespace {
19
20using Sample = base::HistogramBase::Sample;
21
22// If many users have more devices, this number will be increased.
23// But the number is expected to be big enough for now.
24const Sample kMaxUmaDevices = 31;
25
26// Used to count events for usage histogram.
27enum class Usage {
28 CREATED,
29 CREATED_ON_UNSUPPORTED_PLATFORMS,
30 SESSION_STARTED,
31 SESSION_ENDED,
32 INITIALIZED,
33 INPUT_PORT_ADDED,
34 OUTPUT_PORT_ADDED,
35
36 // New items should be inserted here, and |MAX| should point the last item.
37 MAX = INITIALIZED,
38};
39
toyoshim7a809662015-10-06 00:54:21 -070040// Used in StartSession.
41enum class Completion {
42 COMPLETE_SYNCHRONOUSLY,
43 INVOKE_INITIALIZATION,
44 COMPLETE_ASYNCHRONOUSLY,
45};
46
toyoshim715385c2015-08-09 23:00:26 -070047void ReportUsage(Usage usage) {
48 UMA_HISTOGRAM_ENUMERATION("Media.Midi.Usage",
49 static_cast<Sample>(usage),
50 static_cast<Sample>(Usage::MAX) + 1);
51}
52
53} // namespace
54
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000055MidiManager::MidiManager()
toyoshim7a809662015-10-06 00:54:21 -070056 : initialized_(false), finalized_(false), result_(Result::NOT_INITIALIZED) {
toyoshim715385c2015-08-09 23:00:26 -070057 ReportUsage(Usage::CREATED);
crogers@google.com27356e42013-06-22 04:03:03 +000058}
59
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000060MidiManager::~MidiManager() {
toyoshim7a809662015-10-06 00:54:21 -070061 // Make sure that Finalize() is called to clean up resources allocated on
62 // the Chrome_IOThread.
toyoshim131beb52016-07-25 00:42:41 -070063 base::AutoLock auto_lock(lock_);
64 CHECK(finalized_);
yukawa@chromium.orgdb7ad8b2013-12-04 15:42:41 +000065}
crogers@google.com27356e42013-06-22 04:03:03 +000066
agoode7de413f2015-04-24 00:13:39 -070067#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
68 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000069MidiManager* MidiManager::Create() {
toyoshim715385c2015-08-09 23:00:26 -070070 ReportUsage(Usage::CREATED_ON_UNSUPPORTED_PLATFORMS);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000071 return new MidiManager;
72}
73#endif
74
toyoshim7a809662015-10-06 00:54:21 -070075void MidiManager::Shutdown() {
76 UMA_HISTOGRAM_ENUMERATION("Media.Midi.ResultOnShutdown",
77 static_cast<int>(result_),
78 static_cast<int>(Result::MAX) + 1);
79 base::AutoLock auto_lock(lock_);
80 if (session_thread_runner_) {
81 session_thread_runner_->PostTask(
82 FROM_HERE, base::Bind(&MidiManager::ShutdownOnSessionThread,
83 base::Unretained(this)));
84 session_thread_runner_ = nullptr;
85 } else {
86 finalized_ = true;
87 }
88}
89
toyoshima485ff92014-10-23 00:17:59 -070090void MidiManager::StartSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -070091 ReportUsage(Usage::SESSION_STARTED);
92
toyoshim7a809662015-10-06 00:54:21 -070093 Completion completion = Completion::COMPLETE_SYNCHRONOUSLY;
94 Result result = Result::NOT_INITIALIZED;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000095
96 {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000097 base::AutoLock auto_lock(lock_);
toyoshima485ff92014-10-23 00:17:59 -070098 if (clients_.find(client) != clients_.end() ||
99 pending_clients_.find(client) != pending_clients_.end()) {
100 // Should not happen. But just in case the renderer is compromised.
101 NOTREACHED();
102 return;
103 }
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000104
toyoshim7a809662015-10-06 00:54:21 -0700105 if (initialized_) {
106 // Platform dependent initialization was already finished for previously
107 // initialized clients.
108 if (result_ == Result::OK) {
109 AddInitialPorts(client);
110 clients_.insert(client);
111 }
112 // Complete synchronously with |result_|;
113 result = result_;
114 } else {
115 bool too_many_pending_clients_exist =
116 pending_clients_.size() >= kMaxPendingClientCount;
117 // Do not accept a new request if the pending client list contains too
118 // many clients, or Shutdown() was already called.
119 if (too_many_pending_clients_exist || finalized_) {
120 result = Result::INITIALIZATION_ERROR;
121 } else {
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000122 // Call StartInitialization() only for the first request.
toyoshim7a809662015-10-06 00:54:21 -0700123 if (pending_clients_.empty()) {
124 completion = Completion::INVOKE_INITIALIZATION;
fdoraya05bf572016-06-09 08:42:38 -0700125 session_thread_runner_ = base::ThreadTaskRunnerHandle::Get();
toyoshim7a809662015-10-06 00:54:21 -0700126 } else {
127 completion = Completion::COMPLETE_ASYNCHRONOUSLY;
128 }
toyoshima485ff92014-10-23 00:17:59 -0700129 pending_clients_.insert(client);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000130 }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000131 }
ochangae3bae92016-01-12 19:42:10 -0800132
133 if (completion == Completion::COMPLETE_SYNCHRONOUSLY) {
134 client->CompleteStartSession(result);
135 return;
136 }
toyoshim@chromium.orgf77a1e62014-04-11 13:16:24 +0000137 }
crogers@google.com27356e42013-06-22 04:03:03 +0000138
ochangae3bae92016-01-12 19:42:10 -0800139 if (completion == Completion::INVOKE_INITIALIZATION) {
toyoshim7a809662015-10-06 00:54:21 -0700140 // Lazily initialize the MIDI back-end.
141 TRACE_EVENT0("midi", "MidiManager::StartInitialization");
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000142 // CompleteInitialization() will be called asynchronously when platform
143 // dependent initialization is finished.
toyoshim7a809662015-10-06 00:54:21 -0700144 StartInitialization();
crogers@google.com27356e42013-06-22 04:03:03 +0000145 }
crogers@google.com27356e42013-06-22 04:03:03 +0000146}
147
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000148void MidiManager::EndSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -0700149 ReportUsage(Usage::SESSION_ENDED);
150
toyoshima485ff92014-10-23 00:17:59 -0700151 // At this point, |client| can be in the destruction process, and calling
ochangae3bae92016-01-12 19:42:10 -0800152 // any method of |client| is dangerous. Calls on clients *must* be protected
153 // by |lock_| to prevent race conditions.
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000154 base::AutoLock auto_lock(lock_);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000155 clients_.erase(client);
156 pending_clients_.erase(client);
crogers@google.com27356e42013-06-22 04:03:03 +0000157}
158
toyoshim7dd12482015-04-07 07:02:49 -0700159void MidiManager::AccumulateMidiBytesSent(MidiManagerClient* client, size_t n) {
agoodead116b22015-12-07 00:00:35 -0800160 base::AutoLock auto_lock(lock_);
161 if (clients_.find(client) == clients_.end())
162 return;
163
agoodeb02d0962015-12-07 19:45:54 -0800164 // Continue to hold lock_ here in case another thread is currently doing
165 // EndSession.
toyoshim7dd12482015-04-07 07:02:49 -0700166 client->AccumulateMidiBytesSent(n);
167}
168
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000169void MidiManager::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500170 uint32_t port_index,
171 const std::vector<uint8_t>& data,
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000172 double timestamp) {
173 NOTREACHED();
174}
175
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000176void MidiManager::StartInitialization() {
toyoshimf1b88962015-07-09 14:14:51 -0700177 CompleteInitialization(Result::NOT_SUPPORTED);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000178}
179
toyoshimf1b88962015-07-09 14:14:51 -0700180void MidiManager::CompleteInitialization(Result result) {
toyoshim7a809662015-10-06 00:54:21 -0700181 base::AutoLock auto_lock(lock_);
182 if (session_thread_runner_) {
183 session_thread_runner_->PostTask(
184 FROM_HERE, base::Bind(&MidiManager::CompleteInitializationInternal,
185 base::Unretained(this), result));
186 }
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000187}
188
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000189void MidiManager::AddInputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700190 ReportUsage(Usage::INPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700191 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000192 input_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700193 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700194 client->AddInputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000195}
196
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000197void MidiManager::AddOutputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700198 ReportUsage(Usage::OUTPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700199 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000200 output_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700201 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700202 client->AddOutputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000203}
204
Avi Drissman3528fd02015-12-18 20:11:31 -0500205void MidiManager::SetInputPortState(uint32_t port_index, MidiPortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800206 base::AutoLock auto_lock(lock_);
207 DCHECK_LT(port_index, input_ports_.size());
208 input_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700209 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800210 client->SetInputPortState(port_index, state);
211}
212
Avi Drissman3528fd02015-12-18 20:11:31 -0500213void MidiManager::SetOutputPortState(uint32_t port_index, MidiPortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800214 base::AutoLock auto_lock(lock_);
215 DCHECK_LT(port_index, output_ports_.size());
216 output_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700217 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800218 client->SetOutputPortState(port_index, state);
219}
220
Avi Drissman3528fd02015-12-18 20:11:31 -0500221void MidiManager::ReceiveMidiData(uint32_t port_index,
222 const uint8_t* data,
223 size_t length,
224 double timestamp) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000225 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000226
vmpstr0205abb2016-06-28 18:50:56 -0700227 for (auto* client : clients_)
toyoshima485ff92014-10-23 00:17:59 -0700228 client->ReceiveMidiData(port_index, data, length, timestamp);
crogers@google.com542a43a2013-07-31 05:16:49 +0000229}
230
toyoshimf1b88962015-07-09 14:14:51 -0700231void MidiManager::CompleteInitializationInternal(Result result) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000232 TRACE_EVENT0("midi", "MidiManager::CompleteInitialization");
toyoshim715385c2015-08-09 23:00:26 -0700233 ReportUsage(Usage::INITIALIZED);
234 UMA_HISTOGRAM_ENUMERATION("Media.Midi.InputPorts",
235 static_cast<Sample>(input_ports_.size()),
236 kMaxUmaDevices + 1);
237 UMA_HISTOGRAM_ENUMERATION("Media.Midi.OutputPorts",
238 static_cast<Sample>(output_ports_.size()),
239 kMaxUmaDevices + 1);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000240
241 base::AutoLock auto_lock(lock_);
242 DCHECK(clients_.empty());
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000243 DCHECK(!initialized_);
244 initialized_ = true;
245 result_ = result;
246
vmpstr0205abb2016-06-28 18:50:56 -0700247 for (auto* client : pending_clients_) {
toyoshimf1b88962015-07-09 14:14:51 -0700248 if (result_ == Result::OK) {
toyoshima62d6742014-10-23 09:05:03 -0700249 AddInitialPorts(client);
toyoshima485ff92014-10-23 00:17:59 -0700250 clients_.insert(client);
toyoshima62d6742014-10-23 09:05:03 -0700251 }
toyoshima485ff92014-10-23 00:17:59 -0700252 client->CompleteStartSession(result_);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000253 }
254 pending_clients_.clear();
255}
256
toyoshima62d6742014-10-23 09:05:03 -0700257void MidiManager::AddInitialPorts(MidiManagerClient* client) {
258 lock_.AssertAcquired();
259
260 for (const auto& info : input_ports_)
261 client->AddInputPort(info);
262 for (const auto& info : output_ports_)
263 client->AddOutputPort(info);
264}
265
toyoshim7a809662015-10-06 00:54:21 -0700266void MidiManager::ShutdownOnSessionThread() {
267 Finalize();
268 base::AutoLock auto_lock(lock_);
269 finalized_ = true;
270
271 // Detach all clients so that they do not call MidiManager methods any more.
vmpstr0205abb2016-06-28 18:50:56 -0700272 for (auto* client : clients_)
toyoshim7a809662015-10-06 00:54:21 -0700273 client->Detach();
274}
275
toyoshime147c5e2015-05-07 21:58:31 -0700276} // namespace midi
crogers@google.com27356e42013-06-22 04:03:03 +0000277} // namespace media