blob: b3899e3cbcd252b48f49da14040334970a9b4015 [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 Toyoshima6fb495c2017-06-27 15:25:17 +090087 UMA_HISTOGRAM_ENUMERATION("Media.Midi.ResultOnShutdown", result_,
88 static_cast<Sample>(Result::MAX) + 1);
toyoshimf4d61522017-02-10 02:03:32 -080089 bool shutdown_synchronously = false;
90 {
91 base::AutoLock auto_lock(lock_);
92 if (session_thread_runner_) {
93 if (session_thread_runner_->BelongsToCurrentThread()) {
94 shutdown_synchronously = true;
95 } else {
96 session_thread_runner_->PostTask(
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +000097 FROM_HERE, base::BindOnce(&MidiManager::ShutdownOnSessionThread,
98 base::Unretained(this)));
toyoshimf4d61522017-02-10 02:03:32 -080099 }
100 session_thread_runner_ = nullptr;
101 } else {
102 finalized_ = true;
103 }
toyoshim7a809662015-10-06 00:54:21 -0700104 }
toyoshimf4d61522017-02-10 02:03:32 -0800105 if (shutdown_synchronously)
106 ShutdownOnSessionThread();
toyoshim7a809662015-10-06 00:54:21 -0700107}
108
toyoshima485ff92014-10-23 00:17:59 -0700109void MidiManager::StartSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -0700110 ReportUsage(Usage::SESSION_STARTED);
111
shaochuan8ba1cb62016-10-24 04:34:31 -0700112 bool needs_initialization = false;
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000113
114 {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000115 base::AutoLock auto_lock(lock_);
toyoshima485ff92014-10-23 00:17:59 -0700116 if (clients_.find(client) != clients_.end() ||
117 pending_clients_.find(client) != pending_clients_.end()) {
118 // Should not happen. But just in case the renderer is compromised.
119 NOTREACHED();
120 return;
121 }
toyoshim@chromium.orgfc2002e2014-05-07 08:10:34 +0000122
shaochuan8ba1cb62016-10-24 04:34:31 -0700123 // Do not accept a new request if Shutdown() was already called.
124 if (finalized_) {
125 client->CompleteStartSession(Result::INITIALIZATION_ERROR);
126 return;
127 }
128
129 if (initialization_state_ == InitializationState::COMPLETED) {
toyoshim7a809662015-10-06 00:54:21 -0700130 // Platform dependent initialization was already finished for previously
131 // initialized clients.
Takashi Toyoshimaec05edf2017-11-28 11:21:20 +0000132 if (result_ == Result::OK)
toyoshim7a809662015-10-06 00:54:21 -0700133 AddInitialPorts(client);
Takashi Toyoshimaec05edf2017-11-28 11:21:20 +0000134
toyoshim7a809662015-10-06 00:54:21 -0700135 // Complete synchronously with |result_|;
Takashi Toyoshimaec05edf2017-11-28 11:21:20 +0000136 clients_.insert(client);
shaochuan8ba1cb62016-10-24 04:34:31 -0700137 client->CompleteStartSession(result_);
ochangae3bae92016-01-12 19:42:10 -0800138 return;
139 }
shaochuan8ba1cb62016-10-24 04:34:31 -0700140
141 // Do not accept a new request if the pending client list contains too
142 // many clients.
143 if (pending_clients_.size() >= kMaxPendingClientCount) {
144 client->CompleteStartSession(Result::INITIALIZATION_ERROR);
145 return;
146 }
147
148 if (initialization_state_ == InitializationState::NOT_STARTED) {
149 // Set fields protected by |lock_| here and call StartInitialization()
150 // later.
151 needs_initialization = true;
152 session_thread_runner_ = base::ThreadTaskRunnerHandle::Get();
153 initialization_state_ = InitializationState::STARTED;
154 }
155
156 pending_clients_.insert(client);
toyoshim@chromium.orgf77a1e62014-04-11 13:16:24 +0000157 }
crogers@google.com27356e42013-06-22 04:03:03 +0000158
shaochuan8ba1cb62016-10-24 04:34:31 -0700159 if (needs_initialization) {
toyoshim7a809662015-10-06 00:54:21 -0700160 // Lazily initialize the MIDI back-end.
161 TRACE_EVENT0("midi", "MidiManager::StartInitialization");
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000162 // CompleteInitialization() will be called asynchronously when platform
163 // dependent initialization is finished.
toyoshim7a809662015-10-06 00:54:21 -0700164 StartInitialization();
crogers@google.com27356e42013-06-22 04:03:03 +0000165 }
crogers@google.com27356e42013-06-22 04:03:03 +0000166}
167
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000168bool MidiManager::EndSession(MidiManagerClient* client) {
toyoshim715385c2015-08-09 23:00:26 -0700169 ReportUsage(Usage::SESSION_ENDED);
170
toyoshima485ff92014-10-23 00:17:59 -0700171 // At this point, |client| can be in the destruction process, and calling
ochangae3bae92016-01-12 19:42:10 -0800172 // any method of |client| is dangerous. Calls on clients *must* be protected
173 // by |lock_| to prevent race conditions.
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000174 base::AutoLock auto_lock(lock_);
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000175 if (clients_.find(client) == clients_.end() &&
176 pending_clients_.find(client) == pending_clients_.end()) {
177 return false;
178 }
179
toyoshim@chromium.org1fa678a2014-06-13 09:40:33 +0000180 clients_.erase(client);
181 pending_clients_.erase(client);
Takashi Toyoshima150b4432017-08-21 12:08:09 +0000182 return true;
183}
184
185bool MidiManager::HasOpenSession() {
186 base::AutoLock auto_lock(lock_);
187 return clients_.size() != 0u;
crogers@google.com27356e42013-06-22 04:03:03 +0000188}
189
toyoshim7dd12482015-04-07 07:02:49 -0700190void MidiManager::AccumulateMidiBytesSent(MidiManagerClient* client, size_t n) {
agoodead116b22015-12-07 00:00:35 -0800191 base::AutoLock auto_lock(lock_);
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +0900192 data_sent_ = true;
agoodead116b22015-12-07 00:00:35 -0800193 if (clients_.find(client) == clients_.end())
194 return;
195
agoodeb02d0962015-12-07 19:45:54 -0800196 // Continue to hold lock_ here in case another thread is currently doing
197 // EndSession.
toyoshim7dd12482015-04-07 07:02:49 -0700198 client->AccumulateMidiBytesSent(n);
199}
200
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000201void MidiManager::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500202 uint32_t port_index,
203 const std::vector<uint8_t>& data,
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000204 double timestamp) {
205 NOTREACHED();
206}
207
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000208void MidiManager::StartInitialization() {
toyoshimf1b88962015-07-09 14:14:51 -0700209 CompleteInitialization(Result::NOT_SUPPORTED);
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +0000210}
211
toyoshimf1b88962015-07-09 14:14:51 -0700212void MidiManager::CompleteInitialization(Result result) {
toyoshimd28b59c2017-02-20 11:07:37 -0800213 bool complete_asynchronously = false;
214 {
215 base::AutoLock auto_lock(lock_);
216 if (session_thread_runner_) {
217 if (session_thread_runner_->BelongsToCurrentThread()) {
218 complete_asynchronously = true;
219 } else {
220 session_thread_runner_->PostTask(
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000221 FROM_HERE,
222 base::BindOnce(&MidiManager::CompleteInitializationInternal,
223 base::Unretained(this), result));
toyoshimd28b59c2017-02-20 11:07:37 -0800224 }
225 }
toyoshim7a809662015-10-06 00:54:21 -0700226 }
toyoshimd28b59c2017-02-20 11:07:37 -0800227 if (complete_asynchronously)
228 CompleteInitializationInternal(result);
yhirano@chromium.orgc6d5b7b2013-12-20 07:27:23 +0000229}
230
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000231void MidiManager::AddInputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700232 ReportUsage(Usage::INPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700233 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000234 input_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700235 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700236 client->AddInputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000237}
238
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000239void MidiManager::AddOutputPort(const MidiPortInfo& info) {
toyoshim715385c2015-08-09 23:00:26 -0700240 ReportUsage(Usage::OUTPUT_PORT_ADDED);
toyoshima62d6742014-10-23 09:05:03 -0700241 base::AutoLock auto_lock(lock_);
crogers@google.com27356e42013-06-22 04:03:03 +0000242 output_ports_.push_back(info);
vmpstr0205abb2016-06-28 18:50:56 -0700243 for (auto* client : clients_)
toyoshima62d6742014-10-23 09:05:03 -0700244 client->AddOutputPort(info);
crogers@google.com27356e42013-06-22 04:03:03 +0000245}
246
toyoshimec2570a2016-10-21 02:15:27 -0700247void MidiManager::SetInputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800248 base::AutoLock auto_lock(lock_);
249 DCHECK_LT(port_index, input_ports_.size());
250 input_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700251 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800252 client->SetInputPortState(port_index, state);
253}
254
toyoshimec2570a2016-10-21 02:15:27 -0700255void MidiManager::SetOutputPortState(uint32_t port_index, PortState state) {
toyoshim5c6fe4b2015-02-18 23:28:09 -0800256 base::AutoLock auto_lock(lock_);
257 DCHECK_LT(port_index, output_ports_.size());
258 output_ports_[port_index].state = state;
vmpstr0205abb2016-06-28 18:50:56 -0700259 for (auto* client : clients_)
toyoshim5c6fe4b2015-02-18 23:28:09 -0800260 client->SetOutputPortState(port_index, state);
261}
262
Avi Drissman3528fd02015-12-18 20:11:31 -0500263void MidiManager::ReceiveMidiData(uint32_t port_index,
264 const uint8_t* data,
265 size_t length,
266 double timestamp) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000267 base::AutoLock auto_lock(lock_);
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +0900268 data_received_ = true;
crogers@google.com27356e42013-06-22 04:03:03 +0000269
vmpstr0205abb2016-06-28 18:50:56 -0700270 for (auto* client : clients_)
toyoshima485ff92014-10-23 00:17:59 -0700271 client->ReceiveMidiData(port_index, data, length, timestamp);
crogers@google.com542a43a2013-07-31 05:16:49 +0000272}
273
toyoshimf1b88962015-07-09 14:14:51 -0700274void MidiManager::CompleteInitializationInternal(Result result) {
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000275 TRACE_EVENT0("midi", "MidiManager::CompleteInitialization");
toyoshim715385c2015-08-09 23:00:26 -0700276 ReportUsage(Usage::INITIALIZED);
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +0900277 UMA_HISTOGRAM_ENUMERATION("Media.Midi.InputPorts", input_ports_.size(),
toyoshim715385c2015-08-09 23:00:26 -0700278 kMaxUmaDevices + 1);
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +0900279 UMA_HISTOGRAM_ENUMERATION("Media.Midi.OutputPorts", output_ports_.size(),
toyoshim715385c2015-08-09 23:00:26 -0700280 kMaxUmaDevices + 1);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000281
282 base::AutoLock auto_lock(lock_);
283 DCHECK(clients_.empty());
shaochuan8ba1cb62016-10-24 04:34:31 -0700284 DCHECK_EQ(initialization_state_, InitializationState::STARTED);
285 initialization_state_ = InitializationState::COMPLETED;
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000286 result_ = result;
287
vmpstr0205abb2016-06-28 18:50:56 -0700288 for (auto* client : pending_clients_) {
Takashi Toyoshimaec05edf2017-11-28 11:21:20 +0000289 if (result_ == Result::OK)
toyoshima62d6742014-10-23 09:05:03 -0700290 AddInitialPorts(client);
Takashi Toyoshimaec05edf2017-11-28 11:21:20 +0000291
292 clients_.insert(client);
toyoshima485ff92014-10-23 00:17:59 -0700293 client->CompleteStartSession(result_);
toyoshim@chromium.orgc1e05fb2014-05-06 16:39:20 +0000294 }
295 pending_clients_.clear();
296}
297
toyoshima62d6742014-10-23 09:05:03 -0700298void MidiManager::AddInitialPorts(MidiManagerClient* client) {
299 lock_.AssertAcquired();
300
301 for (const auto& info : input_ports_)
302 client->AddInputPort(info);
303 for (const auto& info : output_ports_)
304 client->AddOutputPort(info);
305}
306
toyoshim7a809662015-10-06 00:54:21 -0700307void MidiManager::ShutdownOnSessionThread() {
308 Finalize();
309 base::AutoLock auto_lock(lock_);
310 finalized_ = true;
311
312 // Detach all clients so that they do not call MidiManager methods any more.
vmpstr0205abb2016-06-28 18:50:56 -0700313 for (auto* client : clients_)
toyoshim7a809662015-10-06 00:54:21 -0700314 client->Detach();
Takashi Toyoshima6fb495c2017-06-27 15:25:17 +0900315
316 UMA_HISTOGRAM_ENUMERATION(
317 "Media.Midi.SendReceiveUsage",
318 data_sent_ ? (data_received_ ? SendReceiveUsage::SENT_AND_RECEIVED
319 : SendReceiveUsage::SENT)
320 : (data_received_ ? SendReceiveUsage::RECEIVED
321 : SendReceiveUsage::NO_USE),
322 static_cast<Sample>(SendReceiveUsage::MAX) + 1);
toyoshim7a809662015-10-06 00:54:21 -0700323}
324
toyoshime147c5e2015-05-07 21:58:31 -0700325} // namespace midi