blob: 662d0268821243dad5a003a88e31a94cd4f2d03d [file] [log] [blame]
yhirano@chromium.org881fec42014-02-12 08:23:48 +00001// Copyright 2014 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
yhiranobc742d82015-09-17 07:41:44 -07005#include "media/midi/midi_manager_android.h"
6
7#include "base/android/build_info.h"
Sebastien Marchand2912d9f2019-01-25 16:49:37 +00008#include "base/bind.h"
toyoshimdf2cb1f2016-11-07 03:45:44 -08009#include "base/feature_list.h"
toyoshim5a4fa952017-02-06 20:47:57 -080010#include "base/metrics/field_trial_params.h"
yhiranobc742d82015-09-17 07:41:44 -070011#include "base/strings/stringprintf.h"
12#include "jni/MidiManagerAndroid_jni.h"
13#include "media/midi/midi_device_android.h"
yhirano@chromium.org881fec42014-02-12 08:23:48 +000014#include "media/midi/midi_manager_usb.h"
yhiranobc742d82015-09-17 07:41:44 -070015#include "media/midi/midi_output_port_android.h"
Takashi Toyoshima596f72b2017-09-25 06:47:48 +000016#include "media/midi/midi_service.h"
yhiranobc742d82015-09-17 07:41:44 -070017#include "media/midi/midi_switches.h"
Takashi Toyoshima596f72b2017-09-25 06:47:48 +000018#include "media/midi/task_service.h"
yhirano@chromium.org881fec42014-02-12 08:23:48 +000019#include "media/midi/usb_midi_device_factory_android.h"
20
tornecaf5d7f2016-08-04 08:59:04 -070021using base::android::JavaParamRef;
toyoshimec2570a2016-10-21 02:15:27 -070022using midi::mojom::PortState;
toyoshim2f3a48f2016-10-17 01:54:13 -070023using midi::mojom::Result;
tornecaf5d7f2016-08-04 08:59:04 -070024
toyoshime147c5e2015-05-07 21:58:31 -070025namespace midi {
yhirano@chromium.org881fec42014-02-12 08:23:48 +000026
toyoshim5a4fa952017-02-06 20:47:57 -080027namespace {
28
Takashi Toyoshimada9a78d2017-11-10 07:10:41 +000029bool HasSystemFeatureMidi() {
30 // MIDI API was added at Android M.
yhiranobc742d82015-09-17 07:41:44 -070031 auto sdk_version = base::android::BuildInfo::GetInstance()->sdk_int();
Takashi Toyoshima80b13172017-09-05 06:30:58 +000032 if (sdk_version < base::android::SDK_VERSION_MARSHMALLOW)
33 return false;
34
Takashi Toyoshimada9a78d2017-11-10 07:10:41 +000035 // Check if the MIDI service actually runs on the system.
36 return Java_MidiManagerAndroid_hasSystemFeatureMidi(
Takashi Toyoshima80b13172017-09-05 06:30:58 +000037 base::android::AttachCurrentThread());
toyoshim5a4fa952017-02-06 20:47:57 -080038}
39
40} // namespace
41
toyoshimf4d61522017-02-10 02:03:32 -080042MidiManager* MidiManager::Create(MidiService* service) {
Takashi Toyoshimada9a78d2017-11-10 07:10:41 +000043 if (HasSystemFeatureMidi())
toyoshimf4d61522017-02-10 02:03:32 -080044 return new MidiManagerAndroid(service);
toyoshim5a4fa952017-02-06 20:47:57 -080045
toyoshimf4d61522017-02-10 02:03:32 -080046 return new MidiManagerUsb(service,
Takashi Toyoshima596f72b2017-09-25 06:47:48 +000047 std::make_unique<UsbMidiDeviceFactoryAndroid>());
yhiranobc742d82015-09-17 07:41:44 -070048}
49
toyoshimf4d61522017-02-10 02:03:32 -080050MidiManagerAndroid::MidiManagerAndroid(MidiService* service)
51 : MidiManager(service) {}
yhiranobc742d82015-09-17 07:41:44 -070052
shaochuan4e376352016-08-23 22:02:45 -070053MidiManagerAndroid::~MidiManagerAndroid() {
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +000054 if (!service()->task_service()->UnbindInstance())
55 return;
56
57 // Finalization steps should be implemented after the UnbindInstance() call.
Yutaka Hiranod7c006f2019-02-05 06:22:14 +000058 JNIEnv* env = base::android::AttachCurrentThread();
59 Java_MidiManagerAndroid_stop(env, raw_manager_);
shaochuan4e376352016-08-23 22:02:45 -070060}
yhiranobc742d82015-09-17 07:41:44 -070061
62void MidiManagerAndroid::StartInitialization() {
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +000063 if (!service()->task_service()->BindInstance())
64 return CompleteInitialization(Result::INITIALIZATION_ERROR);
Takashi Toyoshima596f72b2017-09-25 06:47:48 +000065
yhiranobc742d82015-09-17 07:41:44 -070066 JNIEnv* env = base::android::AttachCurrentThread();
67
68 uintptr_t pointer = reinterpret_cast<uintptr_t>(this);
wnwen42fd6432017-05-15 12:29:14 -070069 raw_manager_.Reset(Java_MidiManagerAndroid_create(env, pointer));
shaochuan4e376352016-08-23 22:02:45 -070070
torne94f81842016-08-16 08:10:44 -070071 Java_MidiManagerAndroid_initialize(env, raw_manager_);
yhiranobc742d82015-09-17 07:41:44 -070072}
73
74void MidiManagerAndroid::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -050075 uint32_t port_index,
76 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +000077 base::TimeTicks timestamp) {
yhiranobc742d82015-09-17 07:41:44 -070078 if (port_index >= all_output_ports_.size()) {
79 // |port_index| is provided by a renderer so we can't believe that it is
80 // in the valid range.
81 return;
82 }
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +000083 if (GetOutputPortState(port_index) == PortState::CONNECTED) {
yhiranobc742d82015-09-17 07:41:44 -070084 // We treat send call as implicit open.
85 // TODO(yhirano): Implement explicit open operation from the renderer.
86 if (all_output_ports_[port_index]->Open()) {
toyoshimec2570a2016-10-21 02:15:27 -070087 SetOutputPortState(port_index, PortState::OPENED);
yhiranobc742d82015-09-17 07:41:44 -070088 } else {
89 // We cannot open the port. It's useless to send data to such a port.
90 return;
91 }
92 }
93
Takashi Toyoshima596f72b2017-09-25 06:47:48 +000094 // output_streams_[port_index] is alive unless MidiManagerAndroid is deleted.
95 // The task posted to the TaskService will be disposed safely after unbinding
96 // the service.
97 base::TimeDelta delay = MidiService::TimestampToTimeDeltaDelay(timestamp);
98 service()->task_service()->PostBoundDelayedTask(
99 TaskService::kDefaultRunnerId,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000100 base::BindOnce(&MidiOutputPortAndroid::Send,
Takashi Toyoshima596f72b2017-09-25 06:47:48 +0000101 base::Unretained(all_output_ports_[port_index]), data),
102 delay);
103 service()->task_service()->PostBoundDelayedTask(
104 TaskService::kDefaultRunnerId,
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000105 base::BindOnce(&MidiManagerAndroid::AccumulateMidiBytesSent,
Takashi Toyoshima596f72b2017-09-25 06:47:48 +0000106 base::Unretained(this), client, data.size()),
107 delay);
yhiranobc742d82015-09-17 07:41:44 -0700108}
109
110void MidiManagerAndroid::OnReceivedData(MidiInputPortAndroid* port,
Avi Drissman3528fd02015-12-18 20:11:31 -0500111 const uint8_t* data,
yhiranobc742d82015-09-17 07:41:44 -0700112 size_t size,
113 base::TimeTicks timestamp) {
114 const auto i = input_port_to_index_.find(port);
115 DCHECK(input_port_to_index_.end() != i);
116 ReceiveMidiData(i->second, data, size, timestamp);
117}
118
tornedb299dd2015-11-25 06:17:43 -0800119void MidiManagerAndroid::OnInitialized(
120 JNIEnv* env,
121 const JavaParamRef<jobject>& caller,
122 const JavaParamRef<jobjectArray>& devices) {
Torne (Richard Coles)3b3dba52019-04-29 20:56:14 +0000123 for (auto raw_device : devices.ReadElements<jobject>()) {
Takashi Toyoshima596f72b2017-09-25 06:47:48 +0000124 AddDevice(std::make_unique<MidiDeviceAndroid>(env, raw_device, this));
yhiranobc742d82015-09-17 07:41:44 -0700125 }
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000126 service()->task_service()->PostBoundTask(
127 TaskService::kDefaultRunnerId,
128 base::BindOnce(&MidiManagerAndroid::CompleteInitialization,
129 base::Unretained(this), Result::OK));
yhiranobc742d82015-09-17 07:41:44 -0700130}
131
yhirano59492032016-10-05 18:26:42 -0700132void MidiManagerAndroid::OnInitializationFailed(
133 JNIEnv* env,
134 const JavaParamRef<jobject>& caller) {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000135 service()->task_service()->PostBoundTask(
136 TaskService::kDefaultRunnerId,
137 base::BindOnce(&MidiManagerAndroid::CompleteInitialization,
138 base::Unretained(this), Result::INITIALIZATION_ERROR));
yhirano59492032016-10-05 18:26:42 -0700139}
140
yhiranobc742d82015-09-17 07:41:44 -0700141void MidiManagerAndroid::OnAttached(JNIEnv* env,
tornedb299dd2015-11-25 06:17:43 -0800142 const JavaParamRef<jobject>& caller,
143 const JavaParamRef<jobject>& raw_device) {
Takashi Toyoshima596f72b2017-09-25 06:47:48 +0000144 AddDevice(std::make_unique<MidiDeviceAndroid>(env, raw_device, this));
yhiranobc742d82015-09-17 07:41:44 -0700145}
146
147void MidiManagerAndroid::OnDetached(JNIEnv* env,
tornedb299dd2015-11-25 06:17:43 -0800148 const JavaParamRef<jobject>& caller,
149 const JavaParamRef<jobject>& raw_device) {
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700150 for (auto& device : devices_) {
yhiranobc742d82015-09-17 07:41:44 -0700151 if (device->HasRawDevice(env, raw_device)) {
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700152 for (auto& port : device->input_ports()) {
153 DCHECK(input_port_to_index_.end() !=
154 input_port_to_index_.find(port.get()));
155 size_t index = input_port_to_index_[port.get()];
toyoshimec2570a2016-10-21 02:15:27 -0700156 SetInputPortState(index, PortState::DISCONNECTED);
yhiranobc742d82015-09-17 07:41:44 -0700157 }
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700158 for (auto& port : device->output_ports()) {
159 DCHECK(output_port_to_index_.end() !=
160 output_port_to_index_.find(port.get()));
161 size_t index = output_port_to_index_[port.get()];
toyoshimec2570a2016-10-21 02:15:27 -0700162 SetOutputPortState(index, PortState::DISCONNECTED);
yhiranobc742d82015-09-17 07:41:44 -0700163 }
164 }
165 }
166}
167
danakj75afea02016-04-25 20:36:04 -0700168void MidiManagerAndroid::AddDevice(std::unique_ptr<MidiDeviceAndroid> device) {
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700169 for (auto& port : device->input_ports()) {
yhiranobc742d82015-09-17 07:41:44 -0700170 // We implicitly open input ports here, because there are no signal
171 // from the renderer when to open.
172 // TODO(yhirano): Implement open operation in Blink.
toyoshimec2570a2016-10-21 02:15:27 -0700173 PortState state = port->Open() ? PortState::OPENED : PortState::CONNECTED;
yhiranobc742d82015-09-17 07:41:44 -0700174
175 const size_t index = all_input_ports_.size();
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700176 all_input_ports_.push_back(port.get());
yhiranobc742d82015-09-17 07:41:44 -0700177 // Port ID must be unique in a MIDI manager. This ID setting is
178 // sufficiently unique although there is no user-friendly meaning.
179 // TODO(yhirano): Use a hashed string as ID.
180 const std::string id(
181 base::StringPrintf("native:port-in-%ld", static_cast<long>(index)));
182
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700183 input_port_to_index_.insert(std::make_pair(port.get(), index));
Adithya Srinivasan33252732018-10-17 15:59:40 +0000184 AddInputPort(mojom::PortInfo(id, device->GetManufacturer(),
185 device->GetProductName(),
186 device->GetDeviceVersion(), state));
yhiranobc742d82015-09-17 07:41:44 -0700187 }
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700188 for (auto& port : device->output_ports()) {
yhiranobc742d82015-09-17 07:41:44 -0700189 const size_t index = all_output_ports_.size();
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700190 all_output_ports_.push_back(port.get());
yhiranobc742d82015-09-17 07:41:44 -0700191
192 // Port ID must be unique in a MIDI manager. This ID setting is
193 // sufficiently unique although there is no user-friendly meaning.
194 // TODO(yhirano): Use a hashed string as ID.
195 const std::string id(
196 base::StringPrintf("native:port-out-%ld", static_cast<long>(index)));
197
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700198 output_port_to_index_.insert(std::make_pair(port.get(), index));
yhiranobc742d82015-09-17 07:41:44 -0700199 AddOutputPort(
Adithya Srinivasan33252732018-10-17 15:59:40 +0000200 mojom::PortInfo(id, device->GetManufacturer(), device->GetProductName(),
201 device->GetDeviceVersion(), PortState::CONNECTED));
yhiranobc742d82015-09-17 07:41:44 -0700202 }
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700203 devices_.push_back(std::move(device));
yhiranobc742d82015-09-17 07:41:44 -0700204}
205
toyoshime147c5e2015-05-07 21:58:31 -0700206} // namespace midi