blob: cf62e189ac95751d59eead93208d9450932a8a17 [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.
toyoshime4aa38e2017-04-09 22:59:52 -070025constexpr Sample kMaxUmaDevices = 31;
toyoshim715385c2015-08-09 23:00:26 -070026
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +090027// Used to count events for usage histogram. The item order should not be
28// changed, and new items should be just appended.
toyoshim715385c2015-08-09 23:00:26 -070029enum class Usage {
30 CREATED,
31 CREATED_ON_UNSUPPORTED_PLATFORMS,
32 SESSION_STARTED,
33 SESSION_ENDED,
34 INITIALIZED,
35 INPUT_PORT_ADDED,
36 OUTPUT_PORT_ADDED,
37
38 // New items should be inserted here, and |MAX| should point the last item.
toyoshime4aa38e2017-04-09 22:59:52 -070039 MAX = OUTPUT_PORT_ADDED,
toyoshim715385c2015-08-09 23:00:26 -070040};
41
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +090042// Used to count events for transaction usage histogram. The item order should
43// not be changed, and new items should be just appended.
44enum class SendReceiveUsage {
45 NO_USE,
46 SENT,
47 RECEIVED,
48 SENT_AND_RECEIVED,
49
50 // New items should be inserted here, and |MAX| should point the last item.
51 MAX = SENT_AND_RECEIVED,
52};
53
toyoshim715385c2015-08-09 23:00:26 -070054void ReportUsage(Usage usage) {
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +090055 UMA_HISTOGRAM_ENUMERATION("Media.Midi.Usage", usage,
toyoshim715385c2015-08-09 23:00:26 -070056 static_cast<Sample>(Usage::MAX) + 1);
57}
58
59} // namespace
60
toyoshimf4d61522017-02-10 02:03:32 -080061MidiManager::MidiManager(MidiService* service)
shaochuan8ba1cb62016-10-24 04:34:31 -070062 : initialization_state_(InitializationState::NOT_STARTED),
63 finalized_(false),
toyoshimf4d61522017-02-10 02:03:32 -080064 result_(Result::NOT_INITIALIZED),
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +090065 data_sent_(false),
66 data_received_(false),
toyoshimf4d61522017-02-10 02:03:32 -080067 service_(service) {
toyoshim715385c2015-08-09 23:00:26 -070068 ReportUsage(Usage::CREATED);
crogers@google.com27356e42013-06-22 04:03:03 +000069}
70
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000071MidiManager::~MidiManager() {
toyoshim7a809662015-10-06 00:54:21 -070072 // Make sure that Finalize() is called to clean up resources allocated on
73 // the Chrome_IOThread.
toyoshim131beb52016-07-25 00:42:41 -070074 base::AutoLock auto_lock(lock_);
75 CHECK(finalized_);
yukawa@chromium.orgdb7ad8b2013-12-04 15:42:41 +000076}
crogers@google.com27356e42013-06-22 04:03:03 +000077
agoode7de413f2015-04-24 00:13:39 -070078#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
79 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
toyoshimf4d61522017-02-10 02:03:32 -080080MidiManager* MidiManager::Create(MidiService* service) {
toyoshim715385c2015-08-09 23:00:26 -070081 ReportUsage(Usage::CREATED_ON_UNSUPPORTED_PLATFORMS);
toyoshimf4d61522017-02-10 02:03:32 -080082 return new MidiManager(service);
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +000083}
84#endif
85
toyoshim7a809662015-10-06 00:54:21 -070086void MidiManager::Shutdown() {
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090087 Finalize();
88
89 base::AutoLock auto_lock(lock_);
90 if (session_thread_runner_)
91 DCHECK(session_thread_runner_->BelongsToCurrentThread());
92
93 finalized_ = true;
94
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +090095 UMA_HISTOGRAM_ENUMERATION("Media.Midi.ResultOnShutdown", result_,
96 static_cast<Sample>(Result::MAX) + 1);
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090097
98 UMA_HISTOGRAM_ENUMERATION(
99 "Media.Midi.SendReceiveUsage",
100 data_sent_ ? (data_received_ ? SendReceiveUsage::SENT_AND_RECEIVED
101 : SendReceiveUsage::SENT)
102 : (data_received_ ? SendReceiveUsage::RECEIVED
103 : SendReceiveUsage::NO_USE),
104 static_cast<Sample>(SendReceiveUsage::MAX) + 1);
105
106 // Detach all clients so that they do not call MidiManager methods any more.
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000107 for (auto* client : pending_clients_)
108 client->Detach();
109 pending_clients_.clear();
Takashi Toyoshima408b79f2018-01-09 20:30:07 +0900110 for (auto* client : clients_)
111 client->Detach();
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000112 clients_.clear();
Takashi Toyoshima408b79f2018-01-09 20:30:07 +0900113
114 session_thread_runner_ = nullptr;
toyoshim7a809662015-10-06 00:54:21 -0700115}
116
toyoshima485ff92014-10-23 00:17:59 -0700117void MidiManager::StartSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -0700118 ReportUsage(Usage::SESSION_STARTED);
119
shaochuan8ba1cb62016-10-24 04:34:31 -0700120 bool needs_initialization = false;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000121
122 {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000123 base::AutoLock auto_lock(lock_);
Takashi Toyoshima408b79f2018-01-09 20:30:07 +0900124 DCHECK(!finalized_);
125
toyoshima485ff92014-10-23 00:17:59 -0700126 if (clients_.find(client) != clients_.end() ||
127 pending_clients_.find(client) != pending_clients_.end()) {
128 // Should not happen. But just in case the renderer is compromised.
129 NOTREACHED();
130 return;
131 }
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000132
shaochuan8ba1cb62016-10-24 04:34:31 -0700133 if (initialization_state_ == InitializationState::COMPLETED) {
toyoshim7a809662015-10-06 00:54:21 -0700134 // Platform dependent initialization was already finished for previously
135 // initialized clients.
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000136 if (result_ == Result::OK) {
137 for (const auto& info : input_ports_)
138 client->AddInputPort(info);
139 for (const auto& info : output_ports_)
140 client->AddOutputPort(info);
141 }
Takashi Toyoshimaec05edf2017-11-28 11:21:20 +0000142
toyoshim7a809662015-10-06 00:54:21 -0700143 // Complete synchronously with |result_|;
Takashi Toyoshimaec05edf2017-11-28 11:21:20 +0000144 clients_.insert(client);
shaochuan8ba1cb62016-10-24 04:34:31 -0700145 client->CompleteStartSession(result_);
ochangae3bae92016-01-12 19:42:10 -0800146 return;
147 }
shaochuan8ba1cb62016-10-24 04:34:31 -0700148
149 // Do not accept a new request if the pending client list contains too
150 // many clients.
151 if (pending_clients_.size() >= kMaxPendingClientCount) {
152 client->CompleteStartSession(Result::INITIALIZATION_ERROR);
153 return;
154 }
155
156 if (initialization_state_ == InitializationState::NOT_STARTED) {
157 // Set fields protected by |lock_| here and call StartInitialization()
158 // later.
159 needs_initialization = true;
160 session_thread_runner_ = base::ThreadTaskRunnerHandle::Get();
161 initialization_state_ = InitializationState::STARTED;
162 }
163
164 pending_clients_.insert(client);
toyoshim@chromium.orgf77a1e62014-04-11 13:16:24 +0000165 }
crogers@google.com27356e42013-06-22 04:03:03 +0000166
shaochuan8ba1cb62016-10-24 04:34:31 -0700167 if (needs_initialization) {
toyoshim7a809662015-10-06 00:54:21 -0700168 // Lazily initialize the MIDI back-end.
169 TRACE_EVENT0("midi", "MidiManager::StartInitialization");
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000170 // CompleteInitialization() will be called asynchronously when platform
171 // dependent initialization is finished.
toyoshim7a809662015-10-06 00:54:21 -0700172 StartInitialization();
crogers@google.com27356e42013-06-22 04:03:03 +0000173 }
crogers@google.com27356e42013-06-22 04:03:03 +0000174}
175
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000176bool MidiManager::EndSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -0700177 ReportUsage(Usage::SESSION_ENDED);
178
toyoshima485ff92014-10-23 00:17:59 -0700179 // At this point, |client| can be in the destruction process, and calling
ochangae3bae92016-01-12 19:42:10 -0800180 // any method of |client| is dangerous. Calls on clients *must* be protected
181 // by |lock_| to prevent race conditions.
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000182 base::AutoLock auto_lock(lock_);
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000183 if (clients_.find(client) == clients_.end() &&
184 pending_clients_.find(client) == pending_clients_.end()) {
185 return false;
186 }
187
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000188 clients_.erase(client);
189 pending_clients_.erase(client);
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000190 return true;
191}
192
193bool MidiManager::HasOpenSession() {
194 base::AutoLock auto_lock(lock_);
195 return clients_.size() != 0u;
crogers@google.com27356e42013-06-22 04:03:03 +0000196}
197
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000198void MidiManager::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500199 uint32_t port_index,
200 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +0000201 base::TimeTicks timestamp) {
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000202 NOTREACHED();
203}
204
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000205void MidiManager::StartInitialization() {
toyoshimf1b88962015-07-09 14:14:51 -0700206 CompleteInitialization(Result::NOT_SUPPORTED);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000207}
208
toyoshimf1b88962015-07-09 14:14:51 -0700209void MidiManager::CompleteInitialization(Result result) {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000210 TRACE_EVENT0("midi", "MidiManager::CompleteInitialization");
211 ReportUsage(Usage::INITIALIZED);
212
213 base::AutoLock auto_lock(lock_);
214 UMA_HISTOGRAM_ENUMERATION("Media.Midi.InputPorts", input_ports_.size(),
215 kMaxUmaDevices + 1);
216 UMA_HISTOGRAM_ENUMERATION("Media.Midi.OutputPorts", output_ports_.size(),
217 kMaxUmaDevices + 1);
218
219 if (!session_thread_runner_)
220 return;
221 DCHECK(session_thread_runner_->BelongsToCurrentThread());
222
223 DCHECK(!finalized_);
224 DCHECK(clients_.empty());
225 DCHECK_EQ(initialization_state_, InitializationState::STARTED);
226 initialization_state_ = InitializationState::COMPLETED;
227 result_ = result;
228
229 for (auto* client : pending_clients_) {
230 if (result_ == Result::OK) {
231 for (const auto& info : input_ports_)
232 client->AddInputPort(info);
233 for (const auto& info : output_ports_)
234 client->AddOutputPort(info);
toyoshimd28b59c2017-02-20 11:07:37 -0800235 }
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000236
237 clients_.insert(client);
238 client->CompleteStartSession(result_);
toyoshim7a809662015-10-06 00:54:21 -0700239 }
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000240 pending_clients_.clear();
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000241}
242
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000243void MidiManager::AddInputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700244 ReportUsage(Usage::INPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700245 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000246 input_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700247 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700248 client->AddInputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000249}
250
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000251void MidiManager::AddOutputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700252 ReportUsage(Usage::OUTPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700253 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000254 output_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700255 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700256 client->AddOutputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000257}
258
toyoshimec2570a2016-10-21 02:15:27 -0700259void MidiManager::SetInputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800260 base::AutoLock auto_lock(lock_);
261 DCHECK_LT(port_index, input_ports_.size());
262 input_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700263 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800264 client->SetInputPortState(port_index, state);
265}
266
toyoshimec2570a2016-10-21 02:15:27 -0700267void MidiManager::SetOutputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800268 base::AutoLock auto_lock(lock_);
269 DCHECK_LT(port_index, output_ports_.size());
270 output_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700271 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800272 client->SetOutputPortState(port_index, state);
273}
274
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000275mojom::PortState MidiManager::GetOutputPortState(uint32_t port_index) {
276 base::AutoLock auto_lock(lock_);
277 DCHECK_LT(port_index, output_ports_.size());
278 return output_ports_[port_index].state;
279}
280
281void MidiManager::AccumulateMidiBytesSent(MidiManagerClient* client, size_t n) {
282 base::AutoLock auto_lock(lock_);
283 data_sent_ = true;
284 if (clients_.find(client) == clients_.end())
285 return;
286
287 // Continue to hold lock_ here in case another thread is currently doing
288 // EndSession.
289 client->AccumulateMidiBytesSent(n);
290}
291
Avi Drissman3528fd02015-12-18 20:11:31 -0500292void MidiManager::ReceiveMidiData(uint32_t port_index,
293 const uint8_t* data,
294 size_t length,
tzik925e2c62018-02-02 07:39:45 +0000295 base::TimeTicks timestamp) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000296 base::AutoLock auto_lock(lock_);
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +0900297 data_received_ = true;
crogers@google.com27356e42013-06-22 04:03:03 +0000298
vmpstr0205abb2016-06-28 18:50:56 -0700299 for (auto* client : clients_)
toyoshima485ff92014-10-23 00:17:59 -0700300 client->ReceiveMidiData(port_index, data, length, timestamp);
crogers@google.com542a43a2013-07-31 05:16:49 +0000301}
302
toyoshime147c5e2015-05-07 21:58:31 -0700303} // namespace midi