blob: 6de6c7763dbc901e6e24d848fbdbcf82d8a64969 [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
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +000061MidiManager::MidiManager(MidiService* service) : service_(service) {
toyoshim715385c2015-08-09 23:00:26 -070062 ReportUsage(Usage::CREATED);
crogers@google.com27356e42013-06-22 04:03:03 +000063}
64
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000065MidiManager::~MidiManager() {
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090066 base::AutoLock auto_lock(lock_);
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +000067 if (session_thread_runner_) {
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090068 DCHECK(session_thread_runner_->BelongsToCurrentThread());
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +000069 session_thread_runner_ = nullptr;
70 }
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090071
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +090072 UMA_HISTOGRAM_ENUMERATION("Media.Midi.ResultOnShutdown", result_,
73 static_cast<Sample>(Result::MAX) + 1);
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090074
75 UMA_HISTOGRAM_ENUMERATION(
76 "Media.Midi.SendReceiveUsage",
77 data_sent_ ? (data_received_ ? SendReceiveUsage::SENT_AND_RECEIVED
78 : SendReceiveUsage::SENT)
79 : (data_received_ ? SendReceiveUsage::RECEIVED
80 : SendReceiveUsage::NO_USE),
81 static_cast<Sample>(SendReceiveUsage::MAX) + 1);
82
83 // Detach all clients so that they do not call MidiManager methods any more.
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +000084 for (auto* client : pending_clients_)
85 client->Detach();
Takashi Toyoshima408b79f2018-01-09 20:30:07 +090086 for (auto* client : clients_)
87 client->Detach();
toyoshim7a809662015-10-06 00:54:21 -070088}
89
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +000090#if !defined(OS_MACOSX) && !defined(OS_WIN) && \
91 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
92MidiManager* MidiManager::Create(MidiService* service) {
93 ReportUsage(Usage::CREATED_ON_UNSUPPORTED_PLATFORMS);
94 return new MidiManager(service);
95}
96#endif
97
toyoshima485ff92014-10-23 00:17:59 -070098void MidiManager::StartSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -070099 ReportUsage(Usage::SESSION_STARTED);
100
shaochuan8ba1cb62016-10-24 04:34:31 -0700101 bool needs_initialization = false;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000102
103 {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000104 base::AutoLock auto_lock(lock_);
Takashi Toyoshima408b79f2018-01-09 20:30:07 +0900105
toyoshima485ff92014-10-23 00:17:59 -0700106 if (clients_.find(client) != clients_.end() ||
107 pending_clients_.find(client) != pending_clients_.end()) {
108 // Should not happen. But just in case the renderer is compromised.
109 NOTREACHED();
110 return;
111 }
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000112
shaochuan8ba1cb62016-10-24 04:34:31 -0700113 if (initialization_state_ == InitializationState::COMPLETED) {
toyoshim7a809662015-10-06 00:54:21 -0700114 // Platform dependent initialization was already finished for previously
115 // initialized clients.
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000116 if (result_ == Result::OK) {
117 for (const auto& info : input_ports_)
118 client->AddInputPort(info);
119 for (const auto& info : output_ports_)
120 client->AddOutputPort(info);
121 }
Takashi Toyoshimaec05edf2017-11-28 11:21:20 +0000122
toyoshim7a809662015-10-06 00:54:21 -0700123 // Complete synchronously with |result_|;
Takashi Toyoshimaec05edf2017-11-28 11:21:20 +0000124 clients_.insert(client);
shaochuan8ba1cb62016-10-24 04:34:31 -0700125 client->CompleteStartSession(result_);
ochangae3bae92016-01-12 19:42:10 -0800126 return;
127 }
shaochuan8ba1cb62016-10-24 04:34:31 -0700128
129 // Do not accept a new request if the pending client list contains too
130 // many clients.
131 if (pending_clients_.size() >= kMaxPendingClientCount) {
132 client->CompleteStartSession(Result::INITIALIZATION_ERROR);
133 return;
134 }
135
136 if (initialization_state_ == InitializationState::NOT_STARTED) {
137 // Set fields protected by |lock_| here and call StartInitialization()
138 // later.
139 needs_initialization = true;
140 session_thread_runner_ = base::ThreadTaskRunnerHandle::Get();
141 initialization_state_ = InitializationState::STARTED;
142 }
143
144 pending_clients_.insert(client);
toyoshim@chromium.orgf77a1e62014-04-11 13:16:24 +0000145 }
crogers@google.com27356e42013-06-22 04:03:03 +0000146
shaochuan8ba1cb62016-10-24 04:34:31 -0700147 if (needs_initialization) {
toyoshim7a809662015-10-06 00:54:21 -0700148 // Lazily initialize the MIDI back-end.
149 TRACE_EVENT0("midi", "MidiManager::StartInitialization");
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000150 // CompleteInitialization() will be called asynchronously when platform
151 // dependent initialization is finished.
toyoshim7a809662015-10-06 00:54:21 -0700152 StartInitialization();
crogers@google.com27356e42013-06-22 04:03:03 +0000153 }
crogers@google.com27356e42013-06-22 04:03:03 +0000154}
155
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000156bool MidiManager::EndSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -0700157 ReportUsage(Usage::SESSION_ENDED);
158
toyoshima485ff92014-10-23 00:17:59 -0700159 // At this point, |client| can be in the destruction process, and calling
ochangae3bae92016-01-12 19:42:10 -0800160 // any method of |client| is dangerous. Calls on clients *must* be protected
161 // by |lock_| to prevent race conditions.
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000162 base::AutoLock auto_lock(lock_);
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000163 if (clients_.find(client) == clients_.end() &&
164 pending_clients_.find(client) == pending_clients_.end()) {
165 return false;
166 }
167
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000168 clients_.erase(client);
169 pending_clients_.erase(client);
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000170 return true;
171}
172
173bool MidiManager::HasOpenSession() {
174 base::AutoLock auto_lock(lock_);
175 return clients_.size() != 0u;
crogers@google.com27356e42013-06-22 04:03:03 +0000176}
177
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000178void MidiManager::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500179 uint32_t port_index,
180 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +0000181 base::TimeTicks timestamp) {
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000182 NOTREACHED();
183}
184
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000185void MidiManager::StartInitialization() {
toyoshimf1b88962015-07-09 14:14:51 -0700186 CompleteInitialization(Result::NOT_SUPPORTED);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000187}
188
toyoshimf1b88962015-07-09 14:14:51 -0700189void MidiManager::CompleteInitialization(Result result) {
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000190 DCHECK_EQ(InitializationState::STARTED, initialization_state_);
191
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000192 TRACE_EVENT0("midi", "MidiManager::CompleteInitialization");
193 ReportUsage(Usage::INITIALIZED);
194
195 base::AutoLock auto_lock(lock_);
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000196 if (!session_thread_runner_)
197 return;
198 DCHECK(session_thread_runner_->BelongsToCurrentThread());
199
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000200 UMA_HISTOGRAM_ENUMERATION("Media.Midi.InputPorts", input_ports_.size(),
201 kMaxUmaDevices + 1);
202 UMA_HISTOGRAM_ENUMERATION("Media.Midi.OutputPorts", output_ports_.size(),
203 kMaxUmaDevices + 1);
204
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000205 DCHECK(clients_.empty());
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000206 initialization_state_ = InitializationState::COMPLETED;
207 result_ = result;
208
209 for (auto* client : pending_clients_) {
210 if (result_ == Result::OK) {
211 for (const auto& info : input_ports_)
212 client->AddInputPort(info);
213 for (const auto& info : output_ports_)
214 client->AddOutputPort(info);
toyoshimd28b59c2017-02-20 11:07:37 -0800215 }
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000216
217 clients_.insert(client);
218 client->CompleteStartSession(result_);
toyoshim7a809662015-10-06 00:54:21 -0700219 }
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000220 pending_clients_.clear();
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000221}
222
Adithya Srinivasan33252732018-10-17 15:59:40 +0000223void MidiManager::AddInputPort(const mojom::PortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700224 ReportUsage(Usage::INPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700225 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000226 input_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700227 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700228 client->AddInputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000229}
230
Adithya Srinivasan33252732018-10-17 15:59:40 +0000231void MidiManager::AddOutputPort(const mojom::PortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700232 ReportUsage(Usage::OUTPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700233 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000234 output_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700235 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700236 client->AddOutputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000237}
238
toyoshimec2570a2016-10-21 02:15:27 -0700239void MidiManager::SetInputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800240 base::AutoLock auto_lock(lock_);
241 DCHECK_LT(port_index, input_ports_.size());
242 input_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700243 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800244 client->SetInputPortState(port_index, state);
245}
246
toyoshimec2570a2016-10-21 02:15:27 -0700247void MidiManager::SetOutputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800248 base::AutoLock auto_lock(lock_);
249 DCHECK_LT(port_index, output_ports_.size());
250 output_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700251 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800252 client->SetOutputPortState(port_index, state);
253}
254
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000255mojom::PortState MidiManager::GetOutputPortState(uint32_t port_index) {
256 base::AutoLock auto_lock(lock_);
257 DCHECK_LT(port_index, output_ports_.size());
258 return output_ports_[port_index].state;
259}
260
261void MidiManager::AccumulateMidiBytesSent(MidiManagerClient* client, size_t n) {
262 base::AutoLock auto_lock(lock_);
263 data_sent_ = true;
264 if (clients_.find(client) == clients_.end())
265 return;
266
267 // Continue to hold lock_ here in case another thread is currently doing
268 // EndSession.
269 client->AccumulateMidiBytesSent(n);
270}
271
Avi Drissman3528fd02015-12-18 20:11:31 -0500272void MidiManager::ReceiveMidiData(uint32_t port_index,
273 const uint8_t* data,
274 size_t length,
tzik925e2c62018-02-02 07:39:45 +0000275 base::TimeTicks timestamp) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000276 base::AutoLock auto_lock(lock_);
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +0900277 data_received_ = true;
crogers@google.com27356e42013-06-22 04:03:03 +0000278
vmpstr0205abb2016-06-28 18:50:56 -0700279 for (auto* client : clients_)
toyoshima485ff92014-10-23 00:17:59 -0700280 client->ReceiveMidiData(port_index, data, length, timestamp);
crogers@google.com542a43a2013-07-31 05:16:49 +0000281}
282
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000283size_t MidiManager::GetClientCountForTesting() {
284 base::AutoLock auto_lock(lock_);
285 return clients_.size();
286}
287
288size_t MidiManager::GetPendingClientCountForTesting() {
289 return pending_clients_.size();
290}
291
toyoshime147c5e2015-05-07 21:58:31 -0700292} // namespace midi