blob: 308982d4edff72d047a44b8dcc22282bd32dbe31 [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
toyoshim7a809662015-10-06 00:54:21 -070041// Used in StartSession.
42enum class Completion {
43 COMPLETE_SYNCHRONOUSLY,
44 INVOKE_INITIALIZATION,
45 COMPLETE_ASYNCHRONOUSLY,
46};
47
toyoshim715385c2015-08-09 23:00:26 -070048void ReportUsage(Usage usage) {
49 UMA_HISTOGRAM_ENUMERATION("Media.Midi.Usage",
50 static_cast<Sample>(usage),
51 static_cast<Sample>(Usage::MAX) + 1);
52}
53
54} // namespace
55
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000056MidiManager::MidiManager()
toyoshim7a809662015-10-06 00:54:21 -070057 : initialized_(false), finalized_(false), result_(Result::NOT_INITIALIZED) {
toyoshim715385c2015-08-09 23:00:26 -070058 ReportUsage(Usage::CREATED);
crogers@google.com27356e42013-06-22 04:03:03 +000059}
60
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000061MidiManager::~MidiManager() {
toyoshim7a809662015-10-06 00:54:21 -070062 // Make sure that Finalize() is called to clean up resources allocated on
63 // the Chrome_IOThread.
toyoshim131beb52016-07-25 00:42:41 -070064 base::AutoLock auto_lock(lock_);
65 CHECK(finalized_);
yukawa@chromium.orgdb7ad8b2013-12-04 15:42:41 +000066}
crogers@google.com27356e42013-06-22 04:03:03 +000067
agoode7de413f2015-04-24 00:13:39 -070068#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
69 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000070MidiManager* MidiManager::Create() {
toyoshim715385c2015-08-09 23:00:26 -070071 ReportUsage(Usage::CREATED_ON_UNSUPPORTED_PLATFORMS);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000072 return new MidiManager;
73}
74#endif
75
toyoshim7a809662015-10-06 00:54:21 -070076void MidiManager::Shutdown() {
77 UMA_HISTOGRAM_ENUMERATION("Media.Midi.ResultOnShutdown",
78 static_cast<int>(result_),
79 static_cast<int>(Result::MAX) + 1);
80 base::AutoLock auto_lock(lock_);
81 if (session_thread_runner_) {
82 session_thread_runner_->PostTask(
83 FROM_HERE, base::Bind(&MidiManager::ShutdownOnSessionThread,
84 base::Unretained(this)));
85 session_thread_runner_ = nullptr;
86 } else {
87 finalized_ = true;
88 }
89}
90
toyoshima485ff92014-10-23 00:17:59 -070091void MidiManager::StartSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -070092 ReportUsage(Usage::SESSION_STARTED);
93
toyoshim7a809662015-10-06 00:54:21 -070094 Completion completion = Completion::COMPLETE_SYNCHRONOUSLY;
95 Result result = Result::NOT_INITIALIZED;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000096
97 {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +000098 base::AutoLock auto_lock(lock_);
toyoshima485ff92014-10-23 00:17:59 -070099 if (clients_.find(client) != clients_.end() ||
100 pending_clients_.find(client) != pending_clients_.end()) {
101 // Should not happen. But just in case the renderer is compromised.
102 NOTREACHED();
103 return;
104 }
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000105
toyoshim7a809662015-10-06 00:54:21 -0700106 if (initialized_) {
107 // 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_|;
114 result = result_;
115 } else {
116 bool too_many_pending_clients_exist =
117 pending_clients_.size() >= kMaxPendingClientCount;
118 // Do not accept a new request if the pending client list contains too
119 // many clients, or Shutdown() was already called.
120 if (too_many_pending_clients_exist || finalized_) {
121 result = Result::INITIALIZATION_ERROR;
122 } else {
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000123 // Call StartInitialization() only for the first request.
toyoshim7a809662015-10-06 00:54:21 -0700124 if (pending_clients_.empty()) {
125 completion = Completion::INVOKE_INITIALIZATION;
fdoraya05bf572016-06-09 08:42:38 -0700126 session_thread_runner_ = base::ThreadTaskRunnerHandle::Get();
toyoshim7a809662015-10-06 00:54:21 -0700127 } else {
128 completion = Completion::COMPLETE_ASYNCHRONOUSLY;
129 }
toyoshima485ff92014-10-23 00:17:59 -0700130 pending_clients_.insert(client);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000131 }
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000132 }
ochangae3bae92016-01-12 19:42:10 -0800133
134 if (completion == Completion::COMPLETE_SYNCHRONOUSLY) {
135 client->CompleteStartSession(result);
136 return;
137 }
toyoshim@chromium.orgf77a1e62014-04-11 13:16:24 +0000138 }
crogers@google.com27356e42013-06-22 04:03:03 +0000139
ochangae3bae92016-01-12 19:42:10 -0800140 if (completion == Completion::INVOKE_INITIALIZATION) {
toyoshim7a809662015-10-06 00:54:21 -0700141 // Lazily initialize the MIDI back-end.
142 TRACE_EVENT0("midi", "MidiManager::StartInitialization");
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000143 // CompleteInitialization() will be called asynchronously when platform
144 // dependent initialization is finished.
toyoshim7a809662015-10-06 00:54:21 -0700145 StartInitialization();
crogers@google.com27356e42013-06-22 04:03:03 +0000146 }
crogers@google.com27356e42013-06-22 04:03:03 +0000147}
148
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000149void MidiManager::EndSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -0700150 ReportUsage(Usage::SESSION_ENDED);
151
toyoshima485ff92014-10-23 00:17:59 -0700152 // At this point, |client| can be in the destruction process, and calling
ochangae3bae92016-01-12 19:42:10 -0800153 // any method of |client| is dangerous. Calls on clients *must* be protected
154 // by |lock_| to prevent race conditions.
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000155 base::AutoLock auto_lock(lock_);
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000156 clients_.erase(client);
157 pending_clients_.erase(client);
crogers@google.com27356e42013-06-22 04:03:03 +0000158}
159
toyoshim7dd12482015-04-07 07:02:49 -0700160void MidiManager::AccumulateMidiBytesSent(MidiManagerClient* client, size_t n) {
agoodead116b22015-12-07 00:00:35 -0800161 base::AutoLock auto_lock(lock_);
162 if (clients_.find(client) == clients_.end())
163 return;
164
agoodeb02d0962015-12-07 19:45:54 -0800165 // Continue to hold lock_ here in case another thread is currently doing
166 // EndSession.
toyoshim7dd12482015-04-07 07:02:49 -0700167 client->AccumulateMidiBytesSent(n);
168}
169
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000170void MidiManager::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500171 uint32_t port_index,
172 const std::vector<uint8_t>& data,
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000173 double timestamp) {
174 NOTREACHED();
175}
176
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000177void MidiManager::StartInitialization() {
toyoshimf1b88962015-07-09 14:14:51 -0700178 CompleteInitialization(Result::NOT_SUPPORTED);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000179}
180
toyoshimf1b88962015-07-09 14:14:51 -0700181void MidiManager::CompleteInitialization(Result result) {
toyoshim7a809662015-10-06 00:54:21 -0700182 base::AutoLock auto_lock(lock_);
183 if (session_thread_runner_) {
184 session_thread_runner_->PostTask(
185 FROM_HERE, base::Bind(&MidiManager::CompleteInitializationInternal,
186 base::Unretained(this), result));
187 }
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000188}
189
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000190void MidiManager::AddInputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700191 ReportUsage(Usage::INPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700192 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000193 input_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700194 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700195 client->AddInputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000196}
197
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000198void MidiManager::AddOutputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700199 ReportUsage(Usage::OUTPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700200 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000201 output_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700202 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700203 client->AddOutputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000204}
205
toyoshimec2570a2016-10-21 02:15:27 -0700206void MidiManager::SetInputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800207 base::AutoLock auto_lock(lock_);
208 DCHECK_LT(port_index, input_ports_.size());
209 input_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700210 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800211 client->SetInputPortState(port_index, state);
212}
213
toyoshimec2570a2016-10-21 02:15:27 -0700214void MidiManager::SetOutputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800215 base::AutoLock auto_lock(lock_);
216 DCHECK_LT(port_index, output_ports_.size());
217 output_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700218 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800219 client->SetOutputPortState(port_index, state);
220}
221
Avi Drissman3528fd02015-12-18 20:11:31 -0500222void MidiManager::ReceiveMidiData(uint32_t port_index,
223 const uint8_t* data,
224 size_t length,
225 double timestamp) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000226 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000227
vmpstr0205abb2016-06-28 18:50:56 -0700228 for (auto* client : clients_)
toyoshima485ff92014-10-23 00:17:59 -0700229 client->ReceiveMidiData(port_index, data, length, timestamp);
crogers@google.com542a43a2013-07-31 05:16:49 +0000230}
231
toyoshimf1b88962015-07-09 14:14:51 -0700232void MidiManager::CompleteInitializationInternal(Result result) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000233 TRACE_EVENT0("midi", "MidiManager::CompleteInitialization");
toyoshim715385c2015-08-09 23:00:26 -0700234 ReportUsage(Usage::INITIALIZED);
235 UMA_HISTOGRAM_ENUMERATION("Media.Midi.InputPorts",
236 static_cast<Sample>(input_ports_.size()),
237 kMaxUmaDevices + 1);
238 UMA_HISTOGRAM_ENUMERATION("Media.Midi.OutputPorts",
239 static_cast<Sample>(output_ports_.size()),
240 kMaxUmaDevices + 1);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000241
242 base::AutoLock auto_lock(lock_);
243 DCHECK(clients_.empty());
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000244 DCHECK(!initialized_);
245 initialized_ = true;
246 result_ = result;
247
vmpstr0205abb2016-06-28 18:50:56 -0700248 for (auto* client : pending_clients_) {
toyoshimf1b88962015-07-09 14:14:51 -0700249 if (result_ == Result::OK) {
toyoshima62d6742014-10-23 09:05:03 -0700250 AddInitialPorts(client);
toyoshima485ff92014-10-23 00:17:59 -0700251 clients_.insert(client);
toyoshima62d6742014-10-23 09:05:03 -0700252 }
toyoshima485ff92014-10-23 00:17:59 -0700253 client->CompleteStartSession(result_);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000254 }
255 pending_clients_.clear();
256}
257
toyoshima62d6742014-10-23 09:05:03 -0700258void MidiManager::AddInitialPorts(MidiManagerClient* client) {
259 lock_.AssertAcquired();
260
261 for (const auto& info : input_ports_)
262 client->AddInputPort(info);
263 for (const auto& info : output_ports_)
264 client->AddOutputPort(info);
265}
266
toyoshim7a809662015-10-06 00:54:21 -0700267void MidiManager::ShutdownOnSessionThread() {
268 Finalize();
269 base::AutoLock auto_lock(lock_);
270 finalized_ = true;
271
272 // Detach all clients so that they do not call MidiManager methods any more.
vmpstr0205abb2016-06-28 18:50:56 -0700273 for (auto* client : clients_)
toyoshim7a809662015-10-06 00:54:21 -0700274 client->Detach();
275}
276
toyoshime147c5e2015-05-07 21:58:31 -0700277} // namespace midi