blob: 8a9eec36cea04a6a20a82e374feea0ba8cc601d6 [file] [log] [blame]
Kevin Rocard4bcd67f2018-02-28 14:33:38 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "StreamHalHidl"
18//#define LOG_NDEBUG 0
19
Kevin Rocard95213bf2018-11-08 17:16:57 -080020#include PATH(android/hardware/audio/FILE_VERSION/IStreamOutCallback.h)
Kevin Rocard4bcd67f2018-02-28 14:33:38 -080021#include <hwbinder/IPCThreadState.h>
Mikhail Naganovac917ac2018-11-28 14:03:52 -080022#include <media/AudioParameter.h>
Kevin Rocard4bcd67f2018-02-28 14:33:38 -080023#include <mediautils/SchedulingPolicyService.h>
24#include <utils/Log.h>
25
26#include "DeviceHalHidl.h"
27#include "EffectHalHidl.h"
Eric Laurent6109cdb2020-11-20 18:41:04 +010028#include "HidlUtils.h"
Kevin Rocard4bcd67f2018-02-28 14:33:38 -080029#include "StreamHalHidl.h"
Kevin Rocardb9cfbf12018-02-23 19:11:06 -080030#include "VersionUtils.h"
Kevin Rocard4bcd67f2018-02-28 14:33:38 -080031
Kevin Rocard4bcd67f2018-02-28 14:33:38 -080032using ::android::hardware::MQDescriptorSync;
33using ::android::hardware::Return;
34using ::android::hardware::Void;
Kevin Rocarddf9b4202018-05-10 19:56:08 -070035
Kevin Rocard4bcd67f2018-02-28 14:33:38 -080036namespace android {
Kevin Rocard070e7512018-05-22 09:29:13 -070037namespace CPP_VERSION {
Kevin Rocard4bcd67f2018-02-28 14:33:38 -080038
Mikhail Naganov595caa32018-12-13 11:08:28 -080039using EffectHalHidl = ::android::effect::CPP_VERSION::EffectHalHidl;
Mikhail Naganov9ccaa162018-12-12 10:27:29 -080040using ReadCommand = ::android::hardware::audio::CPP_VERSION::IStreamIn::ReadCommand;
41
42using namespace ::android::hardware::audio::common::CPP_VERSION;
43using namespace ::android::hardware::audio::CPP_VERSION;
44
Kevin Rocard4bcd67f2018-02-28 14:33:38 -080045StreamHalHidl::StreamHalHidl(IStream *stream)
46 : ConversionHelperHidl("Stream"),
47 mStream(stream),
48 mHalThreadPriority(HAL_THREAD_PRIORITY_DEFAULT),
49 mCachedBufferSize(0){
50
51 // Instrument audio signal power logging.
52 // Note: This assumes channel mask, format, and sample rate do not change after creation.
Andy Hung8c2e5822019-04-01 18:09:07 -070053 if (mStream != nullptr /* && mStreamPowerLog.isUserDebugOrEngBuild() */) {
Kevin Rocard4bcd67f2018-02-28 14:33:38 -080054 // Obtain audio properties (see StreamHalHidl::getAudioProperties() below).
55 Return<void> ret = mStream->getAudioProperties(
Kevin Rocardb9cfbf12018-02-23 19:11:06 -080056 [&](auto sr, auto m, auto f) {
Kevin Rocard4bcd67f2018-02-28 14:33:38 -080057 mStreamPowerLog.init(sr,
58 static_cast<audio_channel_mask_t>(m),
59 static_cast<audio_format_t>(f));
60 });
61 }
62}
63
64StreamHalHidl::~StreamHalHidl() {
65 mStream = nullptr;
66}
67
68status_t StreamHalHidl::getSampleRate(uint32_t *rate) {
69 if (!mStream) return NO_INIT;
70 return processReturn("getSampleRate", mStream->getSampleRate(), rate);
71}
72
73status_t StreamHalHidl::getBufferSize(size_t *size) {
74 if (!mStream) return NO_INIT;
75 status_t status = processReturn("getBufferSize", mStream->getBufferSize(), size);
76 if (status == OK) {
77 mCachedBufferSize = *size;
78 }
79 return status;
80}
81
82status_t StreamHalHidl::getChannelMask(audio_channel_mask_t *mask) {
83 if (!mStream) return NO_INIT;
84 return processReturn("getChannelMask", mStream->getChannelMask(), mask);
85}
86
87status_t StreamHalHidl::getFormat(audio_format_t *format) {
88 if (!mStream) return NO_INIT;
89 return processReturn("getFormat", mStream->getFormat(), format);
90}
91
92status_t StreamHalHidl::getAudioProperties(
93 uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format) {
94 if (!mStream) return NO_INIT;
95 Return<void> ret = mStream->getAudioProperties(
Kevin Rocardb9cfbf12018-02-23 19:11:06 -080096 [&](uint32_t sr, auto m, auto f) {
Kevin Rocard4bcd67f2018-02-28 14:33:38 -080097 *sampleRate = sr;
98 *mask = static_cast<audio_channel_mask_t>(m);
99 *format = static_cast<audio_format_t>(f);
100 });
101 return processReturn("getAudioProperties", ret);
102}
103
104status_t StreamHalHidl::setParameters(const String8& kvPairs) {
105 if (!mStream) return NO_INIT;
106 hidl_vec<ParameterValue> hidlParams;
107 status_t status = parametersFromHal(kvPairs, &hidlParams);
108 if (status != OK) return status;
Kevin Rocardb9cfbf12018-02-23 19:11:06 -0800109 return processReturn("setParameters",
Dean Wheatley7b417a22019-01-31 20:39:42 +1100110 utils::setParameters(mStream, {} /* context */, hidlParams));
Kevin Rocard4bcd67f2018-02-28 14:33:38 -0800111}
112
113status_t StreamHalHidl::getParameters(const String8& keys, String8 *values) {
114 values->clear();
115 if (!mStream) return NO_INIT;
116 hidl_vec<hidl_string> hidlKeys;
117 status_t status = keysFromHal(keys, &hidlKeys);
118 if (status != OK) return status;
119 Result retval;
Kevin Rocardb9cfbf12018-02-23 19:11:06 -0800120 Return<void> ret = utils::getParameters(
121 mStream,
122 {} /* context */,
Kevin Rocard4bcd67f2018-02-28 14:33:38 -0800123 hidlKeys,
124 [&](Result r, const hidl_vec<ParameterValue>& parameters) {
125 retval = r;
126 if (retval == Result::OK) {
127 parametersToHal(parameters, values);
128 }
129 });
130 return processReturn("getParameters", ret, retval);
131}
132
133status_t StreamHalHidl::addEffect(sp<EffectHalInterface> effect) {
134 if (!mStream) return NO_INIT;
135 return processReturn("addEffect", mStream->addEffect(
136 static_cast<EffectHalHidl*>(effect.get())->effectId()));
137}
138
139status_t StreamHalHidl::removeEffect(sp<EffectHalInterface> effect) {
140 if (!mStream) return NO_INIT;
141 return processReturn("removeEffect", mStream->removeEffect(
142 static_cast<EffectHalHidl*>(effect.get())->effectId()));
143}
144
145status_t StreamHalHidl::standby() {
146 if (!mStream) return NO_INIT;
147 return processReturn("standby", mStream->standby());
148}
149
150status_t StreamHalHidl::dump(int fd) {
151 if (!mStream) return NO_INIT;
152 native_handle_t* hidlHandle = native_handle_create(1, 0);
153 hidlHandle->data[0] = fd;
Kevin Rocardb9cfbf12018-02-23 19:11:06 -0800154 Return<void> ret = mStream->debug(hidlHandle, {} /* options */);
Kevin Rocard4bcd67f2018-02-28 14:33:38 -0800155 native_handle_delete(hidlHandle);
156 mStreamPowerLog.dump(fd);
157 return processReturn("dump", ret);
158}
159
160status_t StreamHalHidl::start() {
161 if (!mStream) return NO_INIT;
162 return processReturn("start", mStream->start());
163}
164
165status_t StreamHalHidl::stop() {
166 if (!mStream) return NO_INIT;
167 return processReturn("stop", mStream->stop());
168}
169
170status_t StreamHalHidl::createMmapBuffer(int32_t minSizeFrames,
171 struct audio_mmap_buffer_info *info) {
172 Result retval;
173 Return<void> ret = mStream->createMmapBuffer(
174 minSizeFrames,
175 [&](Result r, const MmapBufferInfo& hidlInfo) {
176 retval = r;
177 if (retval == Result::OK) {
178 const native_handle *handle = hidlInfo.sharedMemory.handle();
179 if (handle->numFds > 0) {
180 info->shared_memory_fd = handle->data[0];
Kevin Rocard3d48dce2018-11-08 17:16:57 -0800181#if MAJOR_VERSION >= 4
Kevin Rocard734334f2018-07-12 19:37:41 -0700182 info->flags = audio_mmap_buffer_flag(hidlInfo.flags);
183#endif
Kevin Rocard4bcd67f2018-02-28 14:33:38 -0800184 info->buffer_size_frames = hidlInfo.bufferSizeFrames;
Kevin Rocard734334f2018-07-12 19:37:41 -0700185 // Negative buffer size frame was a hack in O and P to
186 // indicate that the buffer is shareable to applications
187 if (info->buffer_size_frames < 0) {
188 info->buffer_size_frames *= -1;
189 info->flags = audio_mmap_buffer_flag(
190 info->flags | AUDIO_MMAP_APPLICATION_SHAREABLE);
191 }
Kevin Rocard4bcd67f2018-02-28 14:33:38 -0800192 info->burst_size_frames = hidlInfo.burstSizeFrames;
193 // info->shared_memory_address is not needed in HIDL context
194 info->shared_memory_address = NULL;
195 } else {
196 retval = Result::NOT_INITIALIZED;
197 }
198 }
199 });
200 return processReturn("createMmapBuffer", ret, retval);
201}
202
203status_t StreamHalHidl::getMmapPosition(struct audio_mmap_position *position) {
204 Result retval;
205 Return<void> ret = mStream->getMmapPosition(
206 [&](Result r, const MmapPosition& hidlPosition) {
207 retval = r;
208 if (retval == Result::OK) {
209 position->time_nanoseconds = hidlPosition.timeNanoseconds;
210 position->position_frames = hidlPosition.positionFrames;
211 }
212 });
213 return processReturn("getMmapPosition", ret, retval);
214}
215
216status_t StreamHalHidl::setHalThreadPriority(int priority) {
217 mHalThreadPriority = priority;
218 return OK;
219}
220
221status_t StreamHalHidl::getCachedBufferSize(size_t *size) {
222 if (mCachedBufferSize != 0) {
223 *size = mCachedBufferSize;
224 return OK;
225 }
226 return getBufferSize(size);
227}
228
229bool StreamHalHidl::requestHalThreadPriority(pid_t threadPid, pid_t threadId) {
230 if (mHalThreadPriority == HAL_THREAD_PRIORITY_DEFAULT) {
231 return true;
232 }
233 int err = requestPriority(
234 threadPid, threadId,
235 mHalThreadPriority, false /*isForApp*/, true /*asynchronous*/);
236 ALOGE_IF(err, "failed to set priority %d for pid %d tid %d; error %d",
237 mHalThreadPriority, threadPid, threadId, err);
238 // Audio will still work, but latency will be higher and sometimes unacceptable.
239 return err == 0;
240}
241
242namespace {
243
244/* Notes on callback ownership.
245
246This is how (Hw)Binder ownership model looks like. The server implementation
247is owned by Binder framework (via sp<>). Proxies are owned by clients.
248When the last proxy disappears, Binder framework releases the server impl.
249
250Thus, it is not needed to keep any references to StreamOutCallback (this is
251the server impl) -- it will live as long as HAL server holds a strong ref to
252IStreamOutCallback proxy. We clear that reference by calling 'clearCallback'
253from the destructor of StreamOutHalHidl.
254
255The callback only keeps a weak reference to the stream. The stream is owned
256by AudioFlinger.
257
258*/
259
260struct StreamOutCallback : public IStreamOutCallback {
261 StreamOutCallback(const wp<StreamOutHalHidl>& stream) : mStream(stream) {}
262
263 // IStreamOutCallback implementation
264 Return<void> onWriteReady() override {
265 sp<StreamOutHalHidl> stream = mStream.promote();
266 if (stream != 0) {
267 stream->onWriteReady();
268 }
269 return Void();
270 }
271
272 Return<void> onDrainReady() override {
273 sp<StreamOutHalHidl> stream = mStream.promote();
274 if (stream != 0) {
275 stream->onDrainReady();
276 }
277 return Void();
278 }
279
280 Return<void> onError() override {
281 sp<StreamOutHalHidl> stream = mStream.promote();
282 if (stream != 0) {
283 stream->onError();
284 }
285 return Void();
286 }
287
288 private:
289 wp<StreamOutHalHidl> mStream;
290};
291
292} // namespace
293
294StreamOutHalHidl::StreamOutHalHidl(const sp<IStreamOut>& stream)
295 : StreamHalHidl(stream.get()), mStream(stream), mWriterClient(0), mEfGroup(nullptr) {
296}
297
298StreamOutHalHidl::~StreamOutHalHidl() {
299 if (mStream != 0) {
300 if (mCallback.unsafe_get()) {
301 processReturn("clearCallback", mStream->clearCallback());
302 }
jiabinf6eb4c32020-02-25 14:06:25 -0800303#if MAJOR_VERSION >= 6
304 if (mEventCallback.unsafe_get() != nullptr) {
305 processReturn("setEventCallback",
306 mStream->setEventCallback(nullptr));
307 }
308#endif
Kevin Rocard4bcd67f2018-02-28 14:33:38 -0800309 processReturn("close", mStream->close());
310 mStream.clear();
311 }
312 mCallback.clear();
jiabinf6eb4c32020-02-25 14:06:25 -0800313 mEventCallback.clear();
Kevin Rocard4bcd67f2018-02-28 14:33:38 -0800314 hardware::IPCThreadState::self()->flushCommands();
315 if (mEfGroup) {
316 EventFlag::deleteEventFlag(&mEfGroup);
317 }
318}
319
320status_t StreamOutHalHidl::getFrameSize(size_t *size) {
321 if (mStream == 0) return NO_INIT;
322 return processReturn("getFrameSize", mStream->getFrameSize(), size);
323}
324
325status_t StreamOutHalHidl::getLatency(uint32_t *latency) {
326 if (mStream == 0) return NO_INIT;
327 if (mWriterClient == gettid() && mCommandMQ) {
328 return callWriterThread(
329 WriteCommand::GET_LATENCY, "getLatency", nullptr, 0,
330 [&](const WriteStatus& writeStatus) {
331 *latency = writeStatus.reply.latencyMs;
332 });
333 } else {
334 return processReturn("getLatency", mStream->getLatency(), latency);
335 }
336}
337
338status_t StreamOutHalHidl::setVolume(float left, float right) {
339 if (mStream == 0) return NO_INIT;
340 return processReturn("setVolume", mStream->setVolume(left, right));
341}
342
Mikhail Naganovac917ac2018-11-28 14:03:52 -0800343#if MAJOR_VERSION == 2
344status_t StreamOutHalHidl::selectPresentation(int presentationId, int programId) {
345 if (mStream == 0) return NO_INIT;
346 std::vector<ParameterValue> parameters;
347 String8 halParameters;
348 parameters.push_back({AudioParameter::keyPresentationId, std::to_string(presentationId)});
349 parameters.push_back({AudioParameter::keyProgramId, std::to_string(programId)});
350 parametersToHal(hidl_vec<ParameterValue>(parameters), &halParameters);
351 return setParameters(halParameters);
352}
Kevin Rocard1cf6b4d2018-11-20 18:05:44 -0800353#elif MAJOR_VERSION >= 4
Mikhail Naganovac917ac2018-11-28 14:03:52 -0800354status_t StreamOutHalHidl::selectPresentation(int presentationId, int programId) {
355 if (mStream == 0) return NO_INIT;
356 return processReturn("selectPresentation",
357 mStream->selectPresentation(presentationId, programId));
358}
359#endif
360
Kevin Rocard4bcd67f2018-02-28 14:33:38 -0800361status_t StreamOutHalHidl::write(const void *buffer, size_t bytes, size_t *written) {
362 if (mStream == 0) return NO_INIT;
363 *written = 0;
364
365 if (bytes == 0 && !mDataMQ) {
366 // Can't determine the size for the MQ buffer. Wait for a non-empty write request.
367 ALOGW_IF(mCallback.unsafe_get(), "First call to async write with 0 bytes");
368 return OK;
369 }
370
371 status_t status;
372 if (!mDataMQ) {
373 // In case if playback starts close to the end of a compressed track, the bytes
374 // that need to be written is less than the actual buffer size. Need to use
375 // full buffer size for the MQ since otherwise after seeking back to the middle
376 // data will be truncated.
377 size_t bufferSize;
378 if ((status = getCachedBufferSize(&bufferSize)) != OK) {
379 return status;
380 }
381 if (bytes > bufferSize) bufferSize = bytes;
382 if ((status = prepareForWriting(bufferSize)) != OK) {
383 return status;
384 }
385 }
386
387 status = callWriterThread(
388 WriteCommand::WRITE, "write", static_cast<const uint8_t*>(buffer), bytes,
389 [&] (const WriteStatus& writeStatus) {
390 *written = writeStatus.reply.written;
391 // Diagnostics of the cause of b/35813113.
392 ALOGE_IF(*written > bytes,
393 "hal reports more bytes written than asked for: %lld > %lld",
394 (long long)*written, (long long)bytes);
395 });
396 mStreamPowerLog.log(buffer, *written);
397 return status;
398}
399
400status_t StreamOutHalHidl::callWriterThread(
401 WriteCommand cmd, const char* cmdName,
402 const uint8_t* data, size_t dataSize, StreamOutHalHidl::WriterCallback callback) {
403 if (!mCommandMQ->write(&cmd)) {
404 ALOGE("command message queue write failed for \"%s\"", cmdName);
405 return -EAGAIN;
406 }
407 if (data != nullptr) {
408 size_t availableToWrite = mDataMQ->availableToWrite();
409 if (dataSize > availableToWrite) {
410 ALOGW("truncating write data from %lld to %lld due to insufficient data queue space",
411 (long long)dataSize, (long long)availableToWrite);
412 dataSize = availableToWrite;
413 }
414 if (!mDataMQ->write(data, dataSize)) {
415 ALOGE("data message queue write failed for \"%s\"", cmdName);
416 }
417 }
418 mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY));
419
420 // TODO: Remove manual event flag handling once blocking MQ is implemented. b/33815422
421 uint32_t efState = 0;
422retry:
423 status_t ret = mEfGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL), &efState);
424 if (efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL)) {
425 WriteStatus writeStatus;
426 writeStatus.retval = Result::NOT_INITIALIZED;
427 if (!mStatusMQ->read(&writeStatus)) {
428 ALOGE("status message read failed for \"%s\"", cmdName);
429 }
430 if (writeStatus.retval == Result::OK) {
431 ret = OK;
432 callback(writeStatus);
433 } else {
434 ret = processReturn(cmdName, writeStatus.retval);
435 }
436 return ret;
437 }
438 if (ret == -EAGAIN || ret == -EINTR) {
439 // Spurious wakeup. This normally retries no more than once.
440 goto retry;
441 }
442 return ret;
443}
444
445status_t StreamOutHalHidl::prepareForWriting(size_t bufferSize) {
446 std::unique_ptr<CommandMQ> tempCommandMQ;
447 std::unique_ptr<DataMQ> tempDataMQ;
448 std::unique_ptr<StatusMQ> tempStatusMQ;
449 Result retval;
450 pid_t halThreadPid, halThreadTid;
451 Return<void> ret = mStream->prepareForWriting(
452 1, bufferSize,
453 [&](Result r,
454 const CommandMQ::Descriptor& commandMQ,
455 const DataMQ::Descriptor& dataMQ,
456 const StatusMQ::Descriptor& statusMQ,
457 const ThreadInfo& halThreadInfo) {
458 retval = r;
459 if (retval == Result::OK) {
460 tempCommandMQ.reset(new CommandMQ(commandMQ));
461 tempDataMQ.reset(new DataMQ(dataMQ));
462 tempStatusMQ.reset(new StatusMQ(statusMQ));
463 if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) {
464 EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup);
465 }
466 halThreadPid = halThreadInfo.pid;
467 halThreadTid = halThreadInfo.tid;
468 }
469 });
470 if (!ret.isOk() || retval != Result::OK) {
471 return processReturn("prepareForWriting", ret, retval);
472 }
473 if (!tempCommandMQ || !tempCommandMQ->isValid() ||
474 !tempDataMQ || !tempDataMQ->isValid() ||
475 !tempStatusMQ || !tempStatusMQ->isValid() ||
476 !mEfGroup) {
477 ALOGE_IF(!tempCommandMQ, "Failed to obtain command message queue for writing");
478 ALOGE_IF(tempCommandMQ && !tempCommandMQ->isValid(),
479 "Command message queue for writing is invalid");
480 ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for writing");
481 ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "Data message queue for writing is invalid");
482 ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for writing");
483 ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(),
484 "Status message queue for writing is invalid");
485 ALOGE_IF(!mEfGroup, "Event flag creation for writing failed");
486 return NO_INIT;
487 }
488 requestHalThreadPriority(halThreadPid, halThreadTid);
489
490 mCommandMQ = std::move(tempCommandMQ);
491 mDataMQ = std::move(tempDataMQ);
492 mStatusMQ = std::move(tempStatusMQ);
493 mWriterClient = gettid();
494 return OK;
495}
496
497status_t StreamOutHalHidl::getRenderPosition(uint32_t *dspFrames) {
498 if (mStream == 0) return NO_INIT;
499 Result retval;
500 Return<void> ret = mStream->getRenderPosition(
501 [&](Result r, uint32_t d) {
502 retval = r;
503 if (retval == Result::OK) {
504 *dspFrames = d;
505 }
506 });
507 return processReturn("getRenderPosition", ret, retval);
508}
509
510status_t StreamOutHalHidl::getNextWriteTimestamp(int64_t *timestamp) {
511 if (mStream == 0) return NO_INIT;
512 Result retval;
513 Return<void> ret = mStream->getNextWriteTimestamp(
514 [&](Result r, int64_t t) {
515 retval = r;
516 if (retval == Result::OK) {
517 *timestamp = t;
518 }
519 });
520 return processReturn("getRenderPosition", ret, retval);
521}
522
523status_t StreamOutHalHidl::setCallback(wp<StreamOutHalInterfaceCallback> callback) {
524 if (mStream == 0) return NO_INIT;
525 status_t status = processReturn(
526 "setCallback", mStream->setCallback(new StreamOutCallback(this)));
527 if (status == OK) {
528 mCallback = callback;
529 }
530 return status;
531}
532
533status_t StreamOutHalHidl::supportsPauseAndResume(bool *supportsPause, bool *supportsResume) {
534 if (mStream == 0) return NO_INIT;
535 Return<void> ret = mStream->supportsPauseAndResume(
536 [&](bool p, bool r) {
537 *supportsPause = p;
538 *supportsResume = r;
539 });
540 return processReturn("supportsPauseAndResume", ret);
541}
542
543status_t StreamOutHalHidl::pause() {
544 if (mStream == 0) return NO_INIT;
545 return processReturn("pause", mStream->pause());
546}
547
548status_t StreamOutHalHidl::resume() {
549 if (mStream == 0) return NO_INIT;
550 return processReturn("pause", mStream->resume());
551}
552
553status_t StreamOutHalHidl::supportsDrain(bool *supportsDrain) {
554 if (mStream == 0) return NO_INIT;
555 return processReturn("supportsDrain", mStream->supportsDrain(), supportsDrain);
556}
557
558status_t StreamOutHalHidl::drain(bool earlyNotify) {
559 if (mStream == 0) return NO_INIT;
560 return processReturn(
561 "drain", mStream->drain(earlyNotify ? AudioDrain::EARLY_NOTIFY : AudioDrain::ALL));
562}
563
564status_t StreamOutHalHidl::flush() {
565 if (mStream == 0) return NO_INIT;
566 return processReturn("pause", mStream->flush());
567}
568
569status_t StreamOutHalHidl::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) {
570 if (mStream == 0) return NO_INIT;
571 if (mWriterClient == gettid() && mCommandMQ) {
572 return callWriterThread(
573 WriteCommand::GET_PRESENTATION_POSITION, "getPresentationPosition", nullptr, 0,
574 [&](const WriteStatus& writeStatus) {
575 *frames = writeStatus.reply.presentationPosition.frames;
576 timestamp->tv_sec = writeStatus.reply.presentationPosition.timeStamp.tvSec;
577 timestamp->tv_nsec = writeStatus.reply.presentationPosition.timeStamp.tvNSec;
578 });
579 } else {
580 Result retval;
581 Return<void> ret = mStream->getPresentationPosition(
582 [&](Result r, uint64_t hidlFrames, const TimeSpec& hidlTimeStamp) {
583 retval = r;
584 if (retval == Result::OK) {
585 *frames = hidlFrames;
586 timestamp->tv_sec = hidlTimeStamp.tvSec;
587 timestamp->tv_nsec = hidlTimeStamp.tvNSec;
588 }
589 });
590 return processReturn("getPresentationPosition", ret, retval);
591 }
592}
593
Kevin Rocard070e7512018-05-22 09:29:13 -0700594#if MAJOR_VERSION == 2
Mikhail Naganov9ccaa162018-12-12 10:27:29 -0800595status_t StreamOutHalHidl::updateSourceMetadata(
596 const StreamOutHalInterface::SourceMetadata& /* sourceMetadata */) {
Kevin Rocard070e7512018-05-22 09:29:13 -0700597 // Audio HAL V2.0 does not support propagating source metadata
598 return INVALID_OPERATION;
599}
Kevin Rocard3d48dce2018-11-08 17:16:57 -0800600#elif MAJOR_VERSION >= 4
Kevin Rocarda8975a72018-03-27 10:16:52 -0700601/** Transform a standard collection to an HIDL vector. */
602template <class Values, class ElementConverter>
603static auto transformToHidlVec(const Values& values, ElementConverter converter) {
604 hidl_vec<decltype(converter(*values.begin()))> result{values.size()};
605 using namespace std;
606 transform(begin(values), end(values), begin(result), converter);
607 return result;
608}
609
Mikhail Naganov9ccaa162018-12-12 10:27:29 -0800610status_t StreamOutHalHidl::updateSourceMetadata(
611 const StreamOutHalInterface::SourceMetadata& sourceMetadata) {
612 CPP_VERSION::SourceMetadata halMetadata = {
Kevin Rocarda8975a72018-03-27 10:16:52 -0700613 .tracks = transformToHidlVec(sourceMetadata.tracks,
Eric Laurent6109cdb2020-11-20 18:41:04 +0100614 [](const playback_track_metadata_v7& metadata) -> PlaybackTrackMetadata {
615 PlaybackTrackMetadata halTrackMetadata = {
616 .usage=static_cast<AudioUsage>(metadata.base.usage),
617 .contentType=static_cast<AudioContentType>(metadata.base.content_type),
618 .gain=metadata.base.gain,
Kevin Rocarda8975a72018-03-27 10:16:52 -0700619 };
Eric Laurent6109cdb2020-11-20 18:41:04 +0100620#if MAJOR_VERSION >= 7
621 HidlUtils::audioChannelMaskFromHal(metadata.channel_mask, false /*isInput*/,
622 &halTrackMetadata.channelMask);
623
624 std::istringstream tags{metadata.tags};
625 std::string tag;
626 while (std::getline(tags, tag, HidlUtils::sAudioTagSeparator)) {
627 if (!tag.empty()) {
628 halTrackMetadata.tags.push_back(tag);
629 }
630 }
631#endif
632 return halTrackMetadata;
Kevin Rocarda8975a72018-03-27 10:16:52 -0700633 })};
634 return processReturn("updateSourceMetadata", mStream->updateSourceMetadata(halMetadata));
635}
Kevin Rocard070e7512018-05-22 09:29:13 -0700636#endif
Kevin Rocarda8975a72018-03-27 10:16:52 -0700637
jiabinf6eb4c32020-02-25 14:06:25 -0800638#if MAJOR_VERSION < 6
Kuowei Li3bea3a42020-08-13 14:44:25 +0800639status_t StreamOutHalHidl::getDualMonoMode(audio_dual_mono_mode_t* mode __unused) {
640 return INVALID_OPERATION;
641}
642
643status_t StreamOutHalHidl::setDualMonoMode(audio_dual_mono_mode_t mode __unused) {
644 return INVALID_OPERATION;
645}
646
647status_t StreamOutHalHidl::getAudioDescriptionMixLevel(float* leveldB __unused) {
648 return INVALID_OPERATION;
649}
650
651status_t StreamOutHalHidl::setAudioDescriptionMixLevel(float leveldB __unused) {
652 return INVALID_OPERATION;
653}
654
655status_t StreamOutHalHidl::getPlaybackRateParameters(
656 audio_playback_rate_t* playbackRate __unused) {
657 return INVALID_OPERATION;
658}
659
660status_t StreamOutHalHidl::setPlaybackRateParameters(
661 const audio_playback_rate_t& playbackRate __unused) {
662 return INVALID_OPERATION;
663}
664
jiabinf6eb4c32020-02-25 14:06:25 -0800665status_t StreamOutHalHidl::setEventCallback(
666 const sp<StreamOutHalInterfaceEventCallback>& callback __unused) {
667 // Codec format callback is supported starting from audio HAL V6.0
668 return INVALID_OPERATION;
669}
670#else
671
Kuowei Li3bea3a42020-08-13 14:44:25 +0800672status_t StreamOutHalHidl::getDualMonoMode(audio_dual_mono_mode_t* mode) {
673 if (mStream == 0) return NO_INIT;
674 Result retval;
675 Return<void> ret = mStream->getDualMonoMode(
676 [&](Result r, DualMonoMode hidlMode) {
677 retval = r;
678 if (retval == Result::OK) {
679 *mode = static_cast<audio_dual_mono_mode_t>(hidlMode);
680 }
681 });
682 return processReturn("getDualMonoMode", ret, retval);
683}
684
685status_t StreamOutHalHidl::setDualMonoMode(audio_dual_mono_mode_t mode) {
686 if (mStream == 0) return NO_INIT;
687 return processReturn(
688 "setDualMonoMode", mStream->setDualMonoMode(static_cast<DualMonoMode>(mode)));
689}
690
691status_t StreamOutHalHidl::getAudioDescriptionMixLevel(float* leveldB) {
692 if (mStream == 0) return NO_INIT;
693 Result retval;
694 Return<void> ret = mStream->getAudioDescriptionMixLevel(
695 [&](Result r, float hidlLeveldB) {
696 retval = r;
697 if (retval == Result::OK) {
698 *leveldB = hidlLeveldB;
699 }
700 });
701 return processReturn("getAudioDescriptionMixLevel", ret, retval);
702}
703
704status_t StreamOutHalHidl::setAudioDescriptionMixLevel(float leveldB) {
705 if (mStream == 0) return NO_INIT;
706 return processReturn(
707 "setAudioDescriptionMixLevel", mStream->setAudioDescriptionMixLevel(leveldB));
708}
709
710status_t StreamOutHalHidl::getPlaybackRateParameters(audio_playback_rate_t* playbackRate) {
711 if (mStream == 0) return NO_INIT;
712 Result retval;
713 Return<void> ret = mStream->getPlaybackRateParameters(
714 [&](Result r, PlaybackRate hidlPlaybackRate) {
715 retval = r;
716 if (retval == Result::OK) {
717 playbackRate->mSpeed = hidlPlaybackRate.speed;
718 playbackRate->mPitch = hidlPlaybackRate.pitch;
719 playbackRate->mStretchMode =
720 static_cast<audio_timestretch_stretch_mode_t>(
721 hidlPlaybackRate.timestretchMode);
722 playbackRate->mFallbackMode =
723 static_cast<audio_timestretch_fallback_mode_t>(
724 hidlPlaybackRate.fallbackMode);
725 }
726 });
727 return processReturn("getPlaybackRateParameters", ret, retval);
728}
729
730status_t StreamOutHalHidl::setPlaybackRateParameters(const audio_playback_rate_t& playbackRate) {
731 if (mStream == 0) return NO_INIT;
732 return processReturn(
733 "setPlaybackRateParameters", mStream->setPlaybackRateParameters(
734 PlaybackRate{playbackRate.mSpeed, playbackRate.mPitch,
735 static_cast<TimestretchMode>(playbackRate.mStretchMode),
736 static_cast<TimestretchFallbackMode>(playbackRate.mFallbackMode)}));
737}
738
jiabinf6eb4c32020-02-25 14:06:25 -0800739#include PATH(android/hardware/audio/FILE_VERSION/IStreamOutEventCallback.h)
740
741namespace {
742
743struct StreamOutEventCallback : public IStreamOutEventCallback {
744 StreamOutEventCallback(const wp<StreamOutHalHidl>& stream) : mStream(stream) {}
745
746 // IStreamOutEventCallback implementation
747 Return<void> onCodecFormatChanged(
748 const android::hardware::hidl_vec<uint8_t>& audioMetadata) override {
749 sp<StreamOutHalHidl> stream = mStream.promote();
750 if (stream != nullptr) {
751 std::basic_string<uint8_t> metadataBs(audioMetadata.begin(), audioMetadata.end());
752 stream->onCodecFormatChanged(metadataBs);
753 }
754 return Void();
755 }
756
757 private:
758 wp<StreamOutHalHidl> mStream;
759};
760
761} // namespace
762
763status_t StreamOutHalHidl::setEventCallback(
764 const sp<StreamOutHalInterfaceEventCallback>& callback) {
765 if (mStream == nullptr) return NO_INIT;
766 mEventCallback = callback;
767 status_t status = processReturn(
768 "setEventCallback",
769 mStream->setEventCallback(
770 callback.get() == nullptr ? nullptr : new StreamOutEventCallback(this)));
771 return status;
772}
773#endif
774
Kevin Rocard4bcd67f2018-02-28 14:33:38 -0800775void StreamOutHalHidl::onWriteReady() {
776 sp<StreamOutHalInterfaceCallback> callback = mCallback.promote();
777 if (callback == 0) return;
778 ALOGV("asyncCallback onWriteReady");
779 callback->onWriteReady();
780}
781
782void StreamOutHalHidl::onDrainReady() {
783 sp<StreamOutHalInterfaceCallback> callback = mCallback.promote();
784 if (callback == 0) return;
785 ALOGV("asyncCallback onDrainReady");
786 callback->onDrainReady();
787}
788
789void StreamOutHalHidl::onError() {
790 sp<StreamOutHalInterfaceCallback> callback = mCallback.promote();
791 if (callback == 0) return;
792 ALOGV("asyncCallback onError");
793 callback->onError();
794}
795
jiabinf6eb4c32020-02-25 14:06:25 -0800796void StreamOutHalHidl::onCodecFormatChanged(const std::basic_string<uint8_t>& metadataBs) {
797 sp<StreamOutHalInterfaceEventCallback> callback = mEventCallback.promote();
798 if (callback == nullptr) return;
799 ALOGV("asyncCodecFormatCallback %s", __func__);
800 callback->onCodecFormatChanged(metadataBs);
801}
802
Kevin Rocard4bcd67f2018-02-28 14:33:38 -0800803
804StreamInHalHidl::StreamInHalHidl(const sp<IStreamIn>& stream)
805 : StreamHalHidl(stream.get()), mStream(stream), mReaderClient(0), mEfGroup(nullptr) {
806}
807
808StreamInHalHidl::~StreamInHalHidl() {
809 if (mStream != 0) {
810 processReturn("close", mStream->close());
811 mStream.clear();
812 hardware::IPCThreadState::self()->flushCommands();
813 }
814 if (mEfGroup) {
815 EventFlag::deleteEventFlag(&mEfGroup);
816 }
817}
818
819status_t StreamInHalHidl::getFrameSize(size_t *size) {
820 if (mStream == 0) return NO_INIT;
821 return processReturn("getFrameSize", mStream->getFrameSize(), size);
822}
823
824status_t StreamInHalHidl::setGain(float gain) {
825 if (mStream == 0) return NO_INIT;
826 return processReturn("setGain", mStream->setGain(gain));
827}
828
829status_t StreamInHalHidl::read(void *buffer, size_t bytes, size_t *read) {
830 if (mStream == 0) return NO_INIT;
831 *read = 0;
832
833 if (bytes == 0 && !mDataMQ) {
834 // Can't determine the size for the MQ buffer. Wait for a non-empty read request.
835 return OK;
836 }
837
838 status_t status;
839 if (!mDataMQ && (status = prepareForReading(bytes)) != OK) {
840 return status;
841 }
842
843 ReadParameters params;
844 params.command = ReadCommand::READ;
845 params.params.read = bytes;
846 status = callReaderThread(params, "read",
847 [&](const ReadStatus& readStatus) {
848 const size_t availToRead = mDataMQ->availableToRead();
849 if (!mDataMQ->read(static_cast<uint8_t*>(buffer), std::min(bytes, availToRead))) {
850 ALOGE("data message queue read failed for \"read\"");
851 }
852 ALOGW_IF(availToRead != readStatus.reply.read,
853 "HAL read report inconsistent: mq = %d, status = %d",
854 (int32_t)availToRead, (int32_t)readStatus.reply.read);
855 *read = readStatus.reply.read;
856 });
857 mStreamPowerLog.log(buffer, *read);
858 return status;
859}
860
861status_t StreamInHalHidl::callReaderThread(
862 const ReadParameters& params, const char* cmdName,
863 StreamInHalHidl::ReaderCallback callback) {
864 if (!mCommandMQ->write(&params)) {
865 ALOGW("command message queue write failed");
866 return -EAGAIN;
867 }
868 mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL));
869
870 // TODO: Remove manual event flag handling once blocking MQ is implemented. b/33815422
871 uint32_t efState = 0;
872retry:
873 status_t ret = mEfGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY), &efState);
874 if (efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY)) {
875 ReadStatus readStatus;
876 readStatus.retval = Result::NOT_INITIALIZED;
877 if (!mStatusMQ->read(&readStatus)) {
878 ALOGE("status message read failed for \"%s\"", cmdName);
879 }
880 if (readStatus.retval == Result::OK) {
881 ret = OK;
882 callback(readStatus);
883 } else {
884 ret = processReturn(cmdName, readStatus.retval);
885 }
886 return ret;
887 }
888 if (ret == -EAGAIN || ret == -EINTR) {
889 // Spurious wakeup. This normally retries no more than once.
890 goto retry;
891 }
892 return ret;
893}
894
895status_t StreamInHalHidl::prepareForReading(size_t bufferSize) {
896 std::unique_ptr<CommandMQ> tempCommandMQ;
897 std::unique_ptr<DataMQ> tempDataMQ;
898 std::unique_ptr<StatusMQ> tempStatusMQ;
899 Result retval;
900 pid_t halThreadPid, halThreadTid;
901 Return<void> ret = mStream->prepareForReading(
902 1, bufferSize,
903 [&](Result r,
904 const CommandMQ::Descriptor& commandMQ,
905 const DataMQ::Descriptor& dataMQ,
906 const StatusMQ::Descriptor& statusMQ,
907 const ThreadInfo& halThreadInfo) {
908 retval = r;
909 if (retval == Result::OK) {
910 tempCommandMQ.reset(new CommandMQ(commandMQ));
911 tempDataMQ.reset(new DataMQ(dataMQ));
912 tempStatusMQ.reset(new StatusMQ(statusMQ));
913 if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) {
914 EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup);
915 }
916 halThreadPid = halThreadInfo.pid;
917 halThreadTid = halThreadInfo.tid;
918 }
919 });
920 if (!ret.isOk() || retval != Result::OK) {
921 return processReturn("prepareForReading", ret, retval);
922 }
923 if (!tempCommandMQ || !tempCommandMQ->isValid() ||
924 !tempDataMQ || !tempDataMQ->isValid() ||
925 !tempStatusMQ || !tempStatusMQ->isValid() ||
926 !mEfGroup) {
927 ALOGE_IF(!tempCommandMQ, "Failed to obtain command message queue for writing");
928 ALOGE_IF(tempCommandMQ && !tempCommandMQ->isValid(),
929 "Command message queue for writing is invalid");
930 ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for reading");
931 ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "Data message queue for reading is invalid");
932 ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for reading");
933 ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(),
934 "Status message queue for reading is invalid");
935 ALOGE_IF(!mEfGroup, "Event flag creation for reading failed");
936 return NO_INIT;
937 }
938 requestHalThreadPriority(halThreadPid, halThreadTid);
939
940 mCommandMQ = std::move(tempCommandMQ);
941 mDataMQ = std::move(tempDataMQ);
942 mStatusMQ = std::move(tempStatusMQ);
943 mReaderClient = gettid();
944 return OK;
945}
946
947status_t StreamInHalHidl::getInputFramesLost(uint32_t *framesLost) {
948 if (mStream == 0) return NO_INIT;
949 return processReturn("getInputFramesLost", mStream->getInputFramesLost(), framesLost);
950}
951
952status_t StreamInHalHidl::getCapturePosition(int64_t *frames, int64_t *time) {
953 if (mStream == 0) return NO_INIT;
954 if (mReaderClient == gettid() && mCommandMQ) {
955 ReadParameters params;
956 params.command = ReadCommand::GET_CAPTURE_POSITION;
957 return callReaderThread(params, "getCapturePosition",
958 [&](const ReadStatus& readStatus) {
959 *frames = readStatus.reply.capturePosition.frames;
960 *time = readStatus.reply.capturePosition.time;
961 });
962 } else {
963 Result retval;
964 Return<void> ret = mStream->getCapturePosition(
965 [&](Result r, uint64_t hidlFrames, uint64_t hidlTime) {
966 retval = r;
967 if (retval == Result::OK) {
968 *frames = hidlFrames;
969 *time = hidlTime;
970 }
971 });
972 return processReturn("getCapturePosition", ret, retval);
973 }
974}
975
Kevin Rocard070e7512018-05-22 09:29:13 -0700976#if MAJOR_VERSION == 2
977status_t StreamInHalHidl::getActiveMicrophones(
978 std::vector<media::MicrophoneInfo> *microphones __unused) {
979 if (mStream == 0) return NO_INIT;
980 return INVALID_OPERATION;
981}
982
Mikhail Naganov9ccaa162018-12-12 10:27:29 -0800983status_t StreamInHalHidl::updateSinkMetadata(
984 const StreamInHalInterface::SinkMetadata& /* sinkMetadata */) {
Kevin Rocard070e7512018-05-22 09:29:13 -0700985 // Audio HAL V2.0 does not support propagating sink metadata
986 return INVALID_OPERATION;
987}
988
Kevin Rocard3d48dce2018-11-08 17:16:57 -0800989#elif MAJOR_VERSION >= 4
jiabin9ff780e2018-03-19 18:19:52 -0700990status_t StreamInHalHidl::getActiveMicrophones(
991 std::vector<media::MicrophoneInfo> *microphonesInfo) {
992 if (!mStream) return NO_INIT;
993 Result retval;
994 Return<void> ret = mStream->getActiveMicrophones(
995 [&](Result r, hidl_vec<MicrophoneInfo> micArrayHal) {
996 retval = r;
997 for (size_t k = 0; k < micArrayHal.size(); k++) {
998 audio_microphone_characteristic_t dst;
999 // convert
1000 microphoneInfoToHal(micArrayHal[k], &dst);
1001 media::MicrophoneInfo microphone = media::MicrophoneInfo(dst);
1002 microphonesInfo->push_back(microphone);
1003 }
1004 });
1005 return processReturn("getActiveMicrophones", ret, retval);
1006}
1007
Mikhail Naganov9ccaa162018-12-12 10:27:29 -08001008status_t StreamInHalHidl::updateSinkMetadata(const
1009 StreamInHalInterface::SinkMetadata& sinkMetadata) {
1010 CPP_VERSION::SinkMetadata halMetadata = {
Kevin Rocarda8975a72018-03-27 10:16:52 -07001011 .tracks = transformToHidlVec(sinkMetadata.tracks,
Eric Laurent6109cdb2020-11-20 18:41:04 +01001012 [](const record_track_metadata_v7& metadata) -> RecordTrackMetadata {
1013 RecordTrackMetadata halTrackMetadata = {
1014 .source=static_cast<AudioSource>(metadata.base.source),
1015 .gain=metadata.base.gain,
Kevin Rocarda8975a72018-03-27 10:16:52 -07001016 };
Eric Laurent6109cdb2020-11-20 18:41:04 +01001017#if MAJOR_VERSION >= 7
1018 HidlUtils::audioChannelMaskFromHal(metadata.channel_mask, true /*isInput*/,
1019 &halTrackMetadata.channelMask);
1020 std::istringstream tags{metadata.tags};
1021 std::string tag;
1022 while (std::getline(tags, tag, HidlUtils::sAudioTagSeparator)) {
1023 if (!tag.empty()) {
1024 halTrackMetadata.tags.push_back(tag);
1025 }
1026 }
1027#endif
1028 return halTrackMetadata;
Kevin Rocarda8975a72018-03-27 10:16:52 -07001029 })};
1030 return processReturn("updateSinkMetadata", mStream->updateSinkMetadata(halMetadata));
1031}
Kevin Rocard070e7512018-05-22 09:29:13 -07001032#endif
Kevin Rocarda8975a72018-03-27 10:16:52 -07001033
Paul McLean03a6e6a2018-12-04 10:54:13 -07001034#if MAJOR_VERSION < 5
Paul McLean12340082019-03-19 09:35:05 -06001035status_t StreamInHalHidl::setPreferredMicrophoneDirection(
1036 audio_microphone_direction_t direction __unused) {
Paul McLean03a6e6a2018-12-04 10:54:13 -07001037 if (mStream == 0) return NO_INIT;
1038 return INVALID_OPERATION;
1039}
1040
Paul McLean12340082019-03-19 09:35:05 -06001041status_t StreamInHalHidl::setPreferredMicrophoneFieldDimension(float zoom __unused) {
Paul McLean03a6e6a2018-12-04 10:54:13 -07001042 if (mStream == 0) return NO_INIT;
1043 return INVALID_OPERATION;
1044}
1045#else
Paul McLean12340082019-03-19 09:35:05 -06001046status_t StreamInHalHidl::setPreferredMicrophoneDirection(audio_microphone_direction_t direction) {
Paul McLean03a6e6a2018-12-04 10:54:13 -07001047 if (!mStream) return NO_INIT;
Paul McLean12340082019-03-19 09:35:05 -06001048 return processReturn("setPreferredMicrophoneDirection",
1049 mStream->setMicrophoneDirection(static_cast<MicrophoneDirection>(direction)));
Paul McLean03a6e6a2018-12-04 10:54:13 -07001050}
1051
Paul McLean12340082019-03-19 09:35:05 -06001052status_t StreamInHalHidl::setPreferredMicrophoneFieldDimension(float zoom) {
Paul McLean03a6e6a2018-12-04 10:54:13 -07001053 if (!mStream) return NO_INIT;
Paul McLean12340082019-03-19 09:35:05 -06001054 return processReturn("setPreferredMicrophoneFieldDimension",
Paul McLean03a6e6a2018-12-04 10:54:13 -07001055 mStream->setMicrophoneFieldDimension(zoom));
1056}
1057#endif
1058
Kevin Rocard070e7512018-05-22 09:29:13 -07001059} // namespace CPP_VERSION
Kevin Rocard4bcd67f2018-02-28 14:33:38 -08001060} // namespace android