blob: 01f400d9cec1070b22b73419b4f7924d2590067a [file] [log] [blame]
Glenn Kastena8190fc2012-12-03 17:06:56 -08001/*
2 * Copyright (C) 2007 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 "AudioTrackShared"
18//#define LOG_NDEBUG 0
19
Chih-Hung Hsiehffe35582018-09-13 13:59:28 -070020#include <android-base/macros.h>
Glenn Kastena8190fc2012-12-03 17:06:56 -080021#include <private/media/AudioTrackShared.h>
22#include <utils/Log.h>
Elliott Hughesee499292014-05-21 17:55:51 -070023
24#include <linux/futex.h>
25#include <sys/syscall.h>
Glenn Kastena8190fc2012-12-03 17:06:56 -080026
27namespace android {
28
Andy Hungcb2129b2014-11-11 12:17:22 -080029// used to clamp a value to size_t. TODO: move to another file.
30template <typename T>
31size_t clampToSize(T x) {
Andy Hung486a7132014-12-22 16:54:21 -080032 return sizeof(T) > sizeof(size_t) && x > (T) SIZE_MAX ? SIZE_MAX : x < 0 ? 0 : (size_t) x;
Andy Hungcb2129b2014-11-11 12:17:22 -080033}
34
Andy Hung9b461582014-12-01 17:56:29 -080035// incrementSequence is used to determine the next sequence value
36// for the loop and position sequence counters. It should return
37// a value between "other" + 1 and "other" + INT32_MAX, the choice of
38// which needs to be the "least recently used" sequence value for "self".
39// In general, this means (new_self) returned is max(self, other) + 1.
Andy Hungd4ee4db2017-07-12 15:26:04 -070040__attribute__((no_sanitize("integer")))
Andy Hung9b461582014-12-01 17:56:29 -080041static uint32_t incrementSequence(uint32_t self, uint32_t other) {
Chad Brubakercb50c542015-10-07 14:20:10 -070042 int32_t diff = (int32_t) self - (int32_t) other;
Andy Hung9b461582014-12-01 17:56:29 -080043 if (diff >= 0 && diff < INT32_MAX) {
44 return self + 1; // we're already ahead of other.
45 }
46 return other + 1; // we're behind, so move just ahead of other.
47}
48
Glenn Kastena8190fc2012-12-03 17:06:56 -080049audio_track_cblk_t::audio_track_cblk_t()
Phil Burke8972b02016-03-04 11:29:57 -080050 : mServer(0), mFutex(0), mMinimum(0)
51 , mVolumeLR(GAIN_MINIFLOAT_PACKED_UNITY), mSampleRate(0), mSendLevel(0)
52 , mBufferSizeInFrames(0)
53 , mFlags(0)
Glenn Kasten9f80dd22012-12-18 15:57:32 -080054{
55 memset(&u, 0, sizeof(u));
56}
57
58// ---------------------------------------------------------------------------
59
60Proxy::Proxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize,
61 bool isOut, bool clientInServer)
62 : mCblk(cblk), mBuffers(buffers), mFrameCount(frameCount), mFrameSize(frameSize),
63 mFrameCountP2(roundup(frameCount)), mIsOut(isOut), mClientInServer(clientInServer),
Glenn Kasten7db7df02013-06-25 16:13:23 -070064 mIsShutdown(false), mUnreleased(0)
Glenn Kastena8190fc2012-12-03 17:06:56 -080065{
66}
67
Glenn Kasten9f80dd22012-12-18 15:57:32 -080068// ---------------------------------------------------------------------------
69
70ClientProxy::ClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
71 size_t frameSize, bool isOut, bool clientInServer)
Phil Burkc0adecb2016-01-08 12:44:11 -080072 : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer)
Phil Burkc0adecb2016-01-08 12:44:11 -080073 , mEpoch(0)
Andy Hung6ae58432016-02-16 18:32:24 -080074 , mTimestampObserver(&cblk->mExtendedTimestampQueue)
Glenn Kastena8190fc2012-12-03 17:06:56 -080075{
Phil Burke8972b02016-03-04 11:29:57 -080076 setBufferSizeInFrames(frameCount);
Glenn Kastena8190fc2012-12-03 17:06:56 -080077}
78
Glenn Kasten9f80dd22012-12-18 15:57:32 -080079const struct timespec ClientProxy::kForever = {INT_MAX /*tv_sec*/, 0 /*tv_nsec*/};
80const struct timespec ClientProxy::kNonBlocking = {0 /*tv_sec*/, 0 /*tv_nsec*/};
81
82#define MEASURE_NS 10000000 // attempt to provide accurate timeouts if requested >= MEASURE_NS
83
84// To facilitate quicker recovery from server failure, this value limits the timeout per each futex
85// wait. However it does not protect infinite timeouts. If defined to be zero, there is no limit.
86// FIXME May not be compatible with audio tunneling requirements where timeout should be in the
87// order of minutes.
88#define MAX_SEC 5
89
Phil Burke8972b02016-03-04 11:29:57 -080090uint32_t ClientProxy::setBufferSizeInFrames(uint32_t size)
91{
Phil Burke8972b02016-03-04 11:29:57 -080092 // The minimum should be greater than zero and less than the size
93 // at which underruns will occur.
Phil Burk26760d12016-03-21 11:53:07 -070094 const uint32_t minimum = 16; // based on AudioMixer::BLOCKSIZE
Phil Burke8972b02016-03-04 11:29:57 -080095 const uint32_t maximum = frameCount();
96 uint32_t clippedSize = size;
Phil Burk26760d12016-03-21 11:53:07 -070097 if (maximum < minimum) {
98 clippedSize = maximum;
99 } else if (clippedSize < minimum) {
Phil Burke8972b02016-03-04 11:29:57 -0800100 clippedSize = minimum;
101 } else if (clippedSize > maximum) {
102 clippedSize = maximum;
103 }
104 // for server to read
105 android_atomic_release_store(clippedSize, (int32_t *)&mCblk->mBufferSizeInFrames);
106 // for client to read
107 mBufferSizeInFrames = clippedSize;
108 return clippedSize;
109}
110
ilewis926b82f2016-03-29 14:50:36 -0700111__attribute__((no_sanitize("integer")))
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800112status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested,
113 struct timespec *elapsed)
Glenn Kastena8190fc2012-12-03 17:06:56 -0800114{
Andy Hung9c64f342017-08-02 18:10:00 -0700115 LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0,
116 "%s: null or zero frame buffer, buffer:%p", __func__, buffer);
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800117 struct timespec total; // total elapsed time spent waiting
118 total.tv_sec = 0;
119 total.tv_nsec = 0;
120 bool measure = elapsed != NULL; // whether to measure total elapsed time spent waiting
Glenn Kastena8190fc2012-12-03 17:06:56 -0800121
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800122 status_t status;
123 enum {
124 TIMEOUT_ZERO, // requested == NULL || *requested == 0
125 TIMEOUT_INFINITE, // *requested == infinity
126 TIMEOUT_FINITE, // 0 < *requested < infinity
127 TIMEOUT_CONTINUE, // additional chances after TIMEOUT_FINITE
128 } timeout;
129 if (requested == NULL) {
130 timeout = TIMEOUT_ZERO;
131 } else if (requested->tv_sec == 0 && requested->tv_nsec == 0) {
132 timeout = TIMEOUT_ZERO;
133 } else if (requested->tv_sec == INT_MAX) {
134 timeout = TIMEOUT_INFINITE;
Glenn Kastena8190fc2012-12-03 17:06:56 -0800135 } else {
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800136 timeout = TIMEOUT_FINITE;
137 if (requested->tv_sec > 0 || requested->tv_nsec >= MEASURE_NS) {
138 measure = true;
139 }
Glenn Kastena8190fc2012-12-03 17:06:56 -0800140 }
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800141 struct timespec before;
142 bool beforeIsValid = false;
143 audio_track_cblk_t* cblk = mCblk;
144 bool ignoreInitialPendingInterrupt = true;
145 // check for shared memory corruption
146 if (mIsShutdown) {
147 status = NO_INIT;
148 goto end;
149 }
150 for (;;) {
Glenn Kasten96f60d82013-07-12 10:21:18 -0700151 int32_t flags = android_atomic_and(~CBLK_INTERRUPT, &cblk->mFlags);
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800152 // check for track invalidation by server, or server death detection
153 if (flags & CBLK_INVALID) {
154 ALOGV("Track invalidated");
155 status = DEAD_OBJECT;
156 goto end;
157 }
Eric Laurent4d231dc2016-03-11 18:38:23 -0800158 if (flags & CBLK_DISABLED) {
159 ALOGV("Track disabled");
160 status = NOT_ENOUGH_DATA;
161 goto end;
162 }
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800163 // check for obtainBuffer interrupted by client
164 if (!ignoreInitialPendingInterrupt && (flags & CBLK_INTERRUPT)) {
165 ALOGV("obtainBuffer() interrupted by client");
166 status = -EINTR;
167 goto end;
168 }
169 ignoreInitialPendingInterrupt = false;
170 // compute number of frames available to write (AudioTrack) or read (AudioRecord)
171 int32_t front;
172 int32_t rear;
173 if (mIsOut) {
174 // The barrier following the read of mFront is probably redundant.
175 // We're about to perform a conditional branch based on 'filled',
176 // which will force the processor to observe the read of mFront
177 // prior to allowing data writes starting at mRaw.
178 // However, the processor may support speculative execution,
179 // and be unable to undo speculative writes into shared memory.
180 // The barrier will prevent such speculative execution.
181 front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
182 rear = cblk->u.mStreaming.mRear;
Glenn Kastena8190fc2012-12-03 17:06:56 -0800183 } else {
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800184 // On the other hand, this barrier is required.
185 rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
186 front = cblk->u.mStreaming.mFront;
187 }
Phil Burkc0adecb2016-01-08 12:44:11 -0800188 // write to rear, read from front
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800189 ssize_t filled = rear - front;
190 // pipe should not be overfull
191 if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
Glenn Kasten6dbb5e32014-05-13 10:38:42 -0700192 if (mIsOut) {
Mark Salyzyn34fb2962014-06-18 16:30:56 -0700193 ALOGE("Shared memory control block is corrupt (filled=%zd, mFrameCount=%zu); "
Glenn Kasten6dbb5e32014-05-13 10:38:42 -0700194 "shutting down", filled, mFrameCount);
195 mIsShutdown = true;
196 status = NO_INIT;
197 goto end;
198 }
199 // for input, sync up on overrun
200 filled = 0;
201 cblk->u.mStreaming.mFront = rear;
202 (void) android_atomic_or(CBLK_OVERRUN, &cblk->mFlags);
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800203 }
Phil Burkc0adecb2016-01-08 12:44:11 -0800204 // Don't allow filling pipe beyond the user settable size.
205 // The calculation for avail can go negative if the buffer size
206 // is suddenly dropped below the amount already in the buffer.
207 // So use a signed calculation to prevent a numeric overflow abort.
Phil Burke8972b02016-03-04 11:29:57 -0800208 ssize_t adjustableSize = (ssize_t) getBufferSizeInFrames();
Phil Burkc0adecb2016-01-08 12:44:11 -0800209 ssize_t avail = (mIsOut) ? adjustableSize - filled : filled;
210 if (avail < 0) {
211 avail = 0;
212 } else if (avail > 0) {
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800213 // 'avail' may be non-contiguous, so return only the first contiguous chunk
Eric Laurentbdd81012016-01-29 15:25:06 -0800214 size_t part1;
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800215 if (mIsOut) {
216 rear &= mFrameCountP2 - 1;
217 part1 = mFrameCountP2 - rear;
218 } else {
219 front &= mFrameCountP2 - 1;
220 part1 = mFrameCountP2 - front;
Glenn Kastena8190fc2012-12-03 17:06:56 -0800221 }
Eric Laurentbdd81012016-01-29 15:25:06 -0800222 if (part1 > (size_t)avail) {
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800223 part1 = avail;
Glenn Kastena8190fc2012-12-03 17:06:56 -0800224 }
Eric Laurentbdd81012016-01-29 15:25:06 -0800225 if (part1 > buffer->mFrameCount) {
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800226 part1 = buffer->mFrameCount;
227 }
Eric Laurentbdd81012016-01-29 15:25:06 -0800228 buffer->mFrameCount = part1;
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800229 buffer->mRaw = part1 > 0 ?
230 &((char *) mBuffers)[(mIsOut ? rear : front) * mFrameSize] : NULL;
231 buffer->mNonContig = avail - part1;
Glenn Kasten7db7df02013-06-25 16:13:23 -0700232 mUnreleased = part1;
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800233 status = NO_ERROR;
234 break;
235 }
236 struct timespec remaining;
237 const struct timespec *ts;
238 switch (timeout) {
239 case TIMEOUT_ZERO:
240 status = WOULD_BLOCK;
241 goto end;
242 case TIMEOUT_INFINITE:
243 ts = NULL;
244 break;
245 case TIMEOUT_FINITE:
246 timeout = TIMEOUT_CONTINUE;
247 if (MAX_SEC == 0) {
248 ts = requested;
249 break;
250 }
Chih-Hung Hsiehffe35582018-09-13 13:59:28 -0700251 FALLTHROUGH_INTENDED;
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800252 case TIMEOUT_CONTINUE:
253 // FIXME we do not retry if requested < 10ms? needs documentation on this state machine
254 if (!measure || requested->tv_sec < total.tv_sec ||
255 (requested->tv_sec == total.tv_sec && requested->tv_nsec <= total.tv_nsec)) {
256 status = TIMED_OUT;
257 goto end;
258 }
259 remaining.tv_sec = requested->tv_sec - total.tv_sec;
260 if ((remaining.tv_nsec = requested->tv_nsec - total.tv_nsec) < 0) {
261 remaining.tv_nsec += 1000000000;
262 remaining.tv_sec++;
263 }
264 if (0 < MAX_SEC && MAX_SEC < remaining.tv_sec) {
265 remaining.tv_sec = MAX_SEC;
266 remaining.tv_nsec = 0;
267 }
268 ts = &remaining;
269 break;
270 default:
Glenn Kastenadad3d72014-02-21 14:51:43 -0800271 LOG_ALWAYS_FATAL("obtainBuffer() timeout=%d", timeout);
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800272 ts = NULL;
273 break;
274 }
Glenn Kasten0d09a9b2013-06-24 12:06:46 -0700275 int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex);
276 if (!(old & CBLK_FUTEX_WAKE)) {
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800277 if (measure && !beforeIsValid) {
278 clock_gettime(CLOCK_MONOTONIC, &before);
279 beforeIsValid = true;
280 }
Elliott Hughesee499292014-05-21 17:55:51 -0700281 errno = 0;
282 (void) syscall(__NR_futex, &cblk->mFutex,
Glenn Kasten0d09a9b2013-06-24 12:06:46 -0700283 mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts);
Leena Winterrowdb463da82015-12-14 15:58:16 -0800284 status_t error = errno; // clock_gettime can affect errno
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800285 // update total elapsed time spent waiting
286 if (measure) {
287 struct timespec after;
288 clock_gettime(CLOCK_MONOTONIC, &after);
289 total.tv_sec += after.tv_sec - before.tv_sec;
Chih-Hung Hsiehbca74292018-08-10 16:06:07 -0700290 // Use auto instead of long to avoid the google-runtime-int warning.
291 auto deltaNs = after.tv_nsec - before.tv_nsec;
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800292 if (deltaNs < 0) {
293 deltaNs += 1000000000;
294 total.tv_sec--;
295 }
296 if ((total.tv_nsec += deltaNs) >= 1000000000) {
297 total.tv_nsec -= 1000000000;
298 total.tv_sec++;
299 }
300 before = after;
301 beforeIsValid = true;
302 }
Leena Winterrowdb463da82015-12-14 15:58:16 -0800303 switch (error) {
Elliott Hughesee499292014-05-21 17:55:51 -0700304 case 0: // normal wakeup by server, or by binderDied()
305 case EWOULDBLOCK: // benign race condition with server
306 case EINTR: // wait was interrupted by signal or other spurious wakeup
307 case ETIMEDOUT: // time-out expired
Glenn Kasten7db7df02013-06-25 16:13:23 -0700308 // FIXME these error/non-0 status are being dropped
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800309 break;
310 default:
Leena Winterrowdb463da82015-12-14 15:58:16 -0800311 status = error;
Elliott Hughesee499292014-05-21 17:55:51 -0700312 ALOGE("%s unexpected error %s", __func__, strerror(status));
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800313 goto end;
314 }
315 }
316 }
317
318end:
319 if (status != NO_ERROR) {
320 buffer->mFrameCount = 0;
321 buffer->mRaw = NULL;
322 buffer->mNonContig = 0;
Glenn Kasten7db7df02013-06-25 16:13:23 -0700323 mUnreleased = 0;
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800324 }
325 if (elapsed != NULL) {
326 *elapsed = total;
327 }
328 if (requested == NULL) {
329 requested = &kNonBlocking;
330 }
331 if (measure) {
Richard Fitzgeraldb1a270d2013-05-14 12:12:21 +0100332 ALOGV("requested %ld.%03ld elapsed %ld.%03ld",
333 requested->tv_sec, requested->tv_nsec / 1000000,
334 total.tv_sec, total.tv_nsec / 1000000);
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800335 }
336 return status;
337}
338
ilewis926b82f2016-03-29 14:50:36 -0700339__attribute__((no_sanitize("integer")))
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800340void ClientProxy::releaseBuffer(Buffer* buffer)
341{
Glenn Kasten7db7df02013-06-25 16:13:23 -0700342 LOG_ALWAYS_FATAL_IF(buffer == NULL);
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800343 size_t stepCount = buffer->mFrameCount;
Glenn Kasten7db7df02013-06-25 16:13:23 -0700344 if (stepCount == 0 || mIsShutdown) {
345 // prevent accidental re-use of buffer
346 buffer->mFrameCount = 0;
347 buffer->mRaw = NULL;
348 buffer->mNonContig = 0;
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800349 return;
350 }
Andy Hung9c64f342017-08-02 18:10:00 -0700351 LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased && mUnreleased <= mFrameCount),
352 "%s: mUnreleased out of range, "
353 "!(stepCount:%zu <= mUnreleased:%zu <= mFrameCount:%zu), BufferSizeInFrames:%u",
354 __func__, stepCount, mUnreleased, mFrameCount, getBufferSizeInFrames());
Glenn Kasten7db7df02013-06-25 16:13:23 -0700355 mUnreleased -= stepCount;
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800356 audio_track_cblk_t* cblk = mCblk;
357 // Both of these barriers are required
358 if (mIsOut) {
359 int32_t rear = cblk->u.mStreaming.mRear;
360 android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear);
361 } else {
362 int32_t front = cblk->u.mStreaming.mFront;
363 android_atomic_release_store(stepCount + front, &cblk->u.mStreaming.mFront);
364 }
365}
366
367void ClientProxy::binderDied()
368{
369 audio_track_cblk_t* cblk = mCblk;
Glenn Kasten96f60d82013-07-12 10:21:18 -0700370 if (!(android_atomic_or(CBLK_INVALID, &cblk->mFlags) & CBLK_INVALID)) {
zunkyu.lee82a69ea2014-11-07 15:47:32 +0900371 android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800372 // it seems that a FUTEX_WAKE_PRIVATE will not wake a FUTEX_WAIT, even within same process
Elliott Hughesee499292014-05-21 17:55:51 -0700373 (void) syscall(__NR_futex, &cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
374 1);
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800375 }
376}
377
378void ClientProxy::interrupt()
379{
380 audio_track_cblk_t* cblk = mCblk;
Glenn Kasten96f60d82013-07-12 10:21:18 -0700381 if (!(android_atomic_or(CBLK_INTERRUPT, &cblk->mFlags) & CBLK_INTERRUPT)) {
zunkyu.lee82a69ea2014-11-07 15:47:32 +0900382 android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
Elliott Hughesee499292014-05-21 17:55:51 -0700383 (void) syscall(__NR_futex, &cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
384 1);
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800385 }
386}
387
Chad Brubaker65dda4f2015-09-22 16:13:30 -0700388__attribute__((no_sanitize("integer")))
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800389size_t ClientProxy::getMisalignment()
390{
391 audio_track_cblk_t* cblk = mCblk;
392 return (mFrameCountP2 - (mIsOut ? cblk->u.mStreaming.mRear : cblk->u.mStreaming.mFront)) &
393 (mFrameCountP2 - 1);
394}
395
396// ---------------------------------------------------------------------------
397
398void AudioTrackClientProxy::flush()
399{
Andy Hung1d3556d2018-03-29 16:30:14 -0700400 sendStreamingFlushStop(true /* flush */);
401}
402
403void AudioTrackClientProxy::stop()
404{
405 sendStreamingFlushStop(false /* flush */);
406}
407
408// Sets the client-written mFlush and mStop positions, which control server behavior.
409//
410// @param flush indicates whether the operation is a flush or stop.
411// A client stop sets mStop to the current write position;
412// the server will not read past this point until start() or subsequent flush().
413// A client flush sets both mStop and mFlush to the current write position.
414// This advances the server read limit (if previously set) and on the next
415// server read advances the server read position to this limit.
416//
417void AudioTrackClientProxy::sendStreamingFlushStop(bool flush)
418{
419 // TODO: Replace this by 64 bit counters - avoids wrap complication.
Glenn Kasten20f51b12014-10-30 10:43:19 -0700420 // This works for mFrameCountP2 <= 2^30
Andy Hunga2d75cd2015-07-15 17:04:20 -0700421 // mFlush is 32 bits concatenated as [ flush_counter ] [ newfront_offset ]
422 // Should newFlush = cblk->u.mStreaming.mRear? Only problem is
423 // if you want to flush twice to the same rear location after a 32 bit wrap.
Andy Hung1d3556d2018-03-29 16:30:14 -0700424
425 const size_t increment = mFrameCountP2 << 1;
426 const size_t mask = increment - 1;
427 // No need for client atomic synchronization on mRear, mStop, mFlush
428 // as AudioTrack client only read/writes to them under client lock. Server only reads.
429 const int32_t rearMasked = mCblk->u.mStreaming.mRear & mask;
430
431 // update stop before flush so that the server front
432 // never advances beyond a (potential) previous stop's rear limit.
433 int32_t stopBits; // the following add can overflow
434 __builtin_add_overflow(mCblk->u.mStreaming.mStop & ~mask, increment, &stopBits);
435 android_atomic_release_store(rearMasked | stopBits, &mCblk->u.mStreaming.mStop);
436
437 if (flush) {
438 int32_t flushBits; // the following add can overflow
439 __builtin_add_overflow(mCblk->u.mStreaming.mFlush & ~mask, increment, &flushBits);
440 android_atomic_release_store(rearMasked | flushBits, &mCblk->u.mStreaming.mFlush);
441 }
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800442}
443
Eric Laurentbfb1b832013-01-07 09:53:42 -0800444bool AudioTrackClientProxy::clearStreamEndDone() {
Glenn Kasten96f60d82013-07-12 10:21:18 -0700445 return (android_atomic_and(~CBLK_STREAM_END_DONE, &mCblk->mFlags) & CBLK_STREAM_END_DONE) != 0;
Eric Laurentbfb1b832013-01-07 09:53:42 -0800446}
447
448bool AudioTrackClientProxy::getStreamEndDone() const {
Glenn Kasten96f60d82013-07-12 10:21:18 -0700449 return (mCblk->mFlags & CBLK_STREAM_END_DONE) != 0;
Eric Laurentbfb1b832013-01-07 09:53:42 -0800450}
451
Richard Fitzgeraldb1a270d2013-05-14 12:12:21 +0100452status_t AudioTrackClientProxy::waitStreamEndDone(const struct timespec *requested)
453{
454 struct timespec total; // total elapsed time spent waiting
455 total.tv_sec = 0;
456 total.tv_nsec = 0;
457 audio_track_cblk_t* cblk = mCblk;
458 status_t status;
459 enum {
460 TIMEOUT_ZERO, // requested == NULL || *requested == 0
461 TIMEOUT_INFINITE, // *requested == infinity
462 TIMEOUT_FINITE, // 0 < *requested < infinity
463 TIMEOUT_CONTINUE, // additional chances after TIMEOUT_FINITE
464 } timeout;
465 if (requested == NULL) {
466 timeout = TIMEOUT_ZERO;
467 } else if (requested->tv_sec == 0 && requested->tv_nsec == 0) {
468 timeout = TIMEOUT_ZERO;
469 } else if (requested->tv_sec == INT_MAX) {
470 timeout = TIMEOUT_INFINITE;
471 } else {
472 timeout = TIMEOUT_FINITE;
473 }
474 for (;;) {
Glenn Kasten96f60d82013-07-12 10:21:18 -0700475 int32_t flags = android_atomic_and(~(CBLK_INTERRUPT|CBLK_STREAM_END_DONE), &cblk->mFlags);
Richard Fitzgeraldb1a270d2013-05-14 12:12:21 +0100476 // check for track invalidation by server, or server death detection
477 if (flags & CBLK_INVALID) {
478 ALOGV("Track invalidated");
479 status = DEAD_OBJECT;
480 goto end;
481 }
Eric Laurent4d231dc2016-03-11 18:38:23 -0800482 // a track is not supposed to underrun at this stage but consider it done
483 if (flags & (CBLK_STREAM_END_DONE | CBLK_DISABLED)) {
Richard Fitzgeraldb1a270d2013-05-14 12:12:21 +0100484 ALOGV("stream end received");
485 status = NO_ERROR;
486 goto end;
487 }
488 // check for obtainBuffer interrupted by client
Richard Fitzgeraldb1a270d2013-05-14 12:12:21 +0100489 if (flags & CBLK_INTERRUPT) {
490 ALOGV("waitStreamEndDone() interrupted by client");
491 status = -EINTR;
492 goto end;
493 }
494 struct timespec remaining;
495 const struct timespec *ts;
496 switch (timeout) {
497 case TIMEOUT_ZERO:
498 status = WOULD_BLOCK;
499 goto end;
500 case TIMEOUT_INFINITE:
501 ts = NULL;
502 break;
503 case TIMEOUT_FINITE:
504 timeout = TIMEOUT_CONTINUE;
505 if (MAX_SEC == 0) {
506 ts = requested;
507 break;
508 }
Chih-Hung Hsiehffe35582018-09-13 13:59:28 -0700509 FALLTHROUGH_INTENDED;
Richard Fitzgeraldb1a270d2013-05-14 12:12:21 +0100510 case TIMEOUT_CONTINUE:
511 // FIXME we do not retry if requested < 10ms? needs documentation on this state machine
512 if (requested->tv_sec < total.tv_sec ||
513 (requested->tv_sec == total.tv_sec && requested->tv_nsec <= total.tv_nsec)) {
514 status = TIMED_OUT;
515 goto end;
516 }
517 remaining.tv_sec = requested->tv_sec - total.tv_sec;
518 if ((remaining.tv_nsec = requested->tv_nsec - total.tv_nsec) < 0) {
519 remaining.tv_nsec += 1000000000;
520 remaining.tv_sec++;
521 }
522 if (0 < MAX_SEC && MAX_SEC < remaining.tv_sec) {
523 remaining.tv_sec = MAX_SEC;
524 remaining.tv_nsec = 0;
525 }
526 ts = &remaining;
527 break;
528 default:
Glenn Kastenadad3d72014-02-21 14:51:43 -0800529 LOG_ALWAYS_FATAL("waitStreamEndDone() timeout=%d", timeout);
Richard Fitzgeraldb1a270d2013-05-14 12:12:21 +0100530 ts = NULL;
531 break;
532 }
533 int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex);
534 if (!(old & CBLK_FUTEX_WAKE)) {
Elliott Hughesee499292014-05-21 17:55:51 -0700535 errno = 0;
536 (void) syscall(__NR_futex, &cblk->mFutex,
Richard Fitzgeraldb1a270d2013-05-14 12:12:21 +0100537 mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts);
Elliott Hughesee499292014-05-21 17:55:51 -0700538 switch (errno) {
539 case 0: // normal wakeup by server, or by binderDied()
540 case EWOULDBLOCK: // benign race condition with server
541 case EINTR: // wait was interrupted by signal or other spurious wakeup
542 case ETIMEDOUT: // time-out expired
Richard Fitzgeraldb1a270d2013-05-14 12:12:21 +0100543 break;
544 default:
Elliott Hughesee499292014-05-21 17:55:51 -0700545 status = errno;
546 ALOGE("%s unexpected error %s", __func__, strerror(status));
Richard Fitzgeraldb1a270d2013-05-14 12:12:21 +0100547 goto end;
548 }
549 }
550 }
551
552end:
553 if (requested == NULL) {
554 requested = &kNonBlocking;
555 }
556 return status;
557}
558
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800559// ---------------------------------------------------------------------------
560
561StaticAudioTrackClientProxy::StaticAudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers,
562 size_t frameCount, size_t frameSize)
563 : AudioTrackClientProxy(cblk, buffers, frameCount, frameSize),
Andy Hung4ede21d2014-12-12 15:37:34 -0800564 mMutator(&cblk->u.mStatic.mSingleStateQueue),
565 mPosLoopObserver(&cblk->u.mStatic.mPosLoopQueue)
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800566{
Andy Hung9b461582014-12-01 17:56:29 -0800567 memset(&mState, 0, sizeof(mState));
Andy Hung4ede21d2014-12-12 15:37:34 -0800568 memset(&mPosLoop, 0, sizeof(mPosLoop));
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800569}
570
571void StaticAudioTrackClientProxy::flush()
572{
Glenn Kastenadad3d72014-02-21 14:51:43 -0800573 LOG_ALWAYS_FATAL("static flush");
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800574}
575
Andy Hung1d3556d2018-03-29 16:30:14 -0700576void StaticAudioTrackClientProxy::stop()
577{
578 ; // no special handling required for static tracks.
579}
580
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800581void StaticAudioTrackClientProxy::setLoop(size_t loopStart, size_t loopEnd, int loopCount)
582{
Glenn Kastenfdac7c02014-01-28 11:03:28 -0800583 // This can only happen on a 64-bit client
584 if (loopStart > UINT32_MAX || loopEnd > UINT32_MAX) {
585 // FIXME Should return an error status
586 return;
587 }
Andy Hung9b461582014-12-01 17:56:29 -0800588 mState.mLoopStart = (uint32_t) loopStart;
589 mState.mLoopEnd = (uint32_t) loopEnd;
590 mState.mLoopCount = loopCount;
591 mState.mLoopSequence = incrementSequence(mState.mLoopSequence, mState.mPositionSequence);
592 // set patch-up variables until the mState is acknowledged by the ServerProxy.
593 // observed buffer position and loop count will freeze until then to give the
594 // illusion of a synchronous change.
Andy Hung4ede21d2014-12-12 15:37:34 -0800595 getBufferPositionAndLoopCount(NULL, NULL);
Andy Hung9b461582014-12-01 17:56:29 -0800596 // preserve behavior to restart at mState.mLoopStart if position exceeds mState.mLoopEnd.
Andy Hung4ede21d2014-12-12 15:37:34 -0800597 if (mState.mLoopCount != 0 && mPosLoop.mBufferPosition >= mState.mLoopEnd) {
598 mPosLoop.mBufferPosition = mState.mLoopStart;
Andy Hung680b7952014-11-12 13:18:52 -0800599 }
Andy Hung4ede21d2014-12-12 15:37:34 -0800600 mPosLoop.mLoopCount = mState.mLoopCount;
Andy Hung9b461582014-12-01 17:56:29 -0800601 (void) mMutator.push(mState);
602}
603
604void StaticAudioTrackClientProxy::setBufferPosition(size_t position)
605{
606 // This can only happen on a 64-bit client
607 if (position > UINT32_MAX) {
608 // FIXME Should return an error status
609 return;
610 }
611 mState.mPosition = (uint32_t) position;
612 mState.mPositionSequence = incrementSequence(mState.mPositionSequence, mState.mLoopSequence);
Andy Hung4ede21d2014-12-12 15:37:34 -0800613 // set patch-up variables until the mState is acknowledged by the ServerProxy.
614 // observed buffer position and loop count will freeze until then to give the
615 // illusion of a synchronous change.
616 if (mState.mLoopCount > 0) { // only check if loop count is changing
617 getBufferPositionAndLoopCount(NULL, NULL); // get last position
618 }
619 mPosLoop.mBufferPosition = position;
620 if (position >= mState.mLoopEnd) {
621 // no ongoing loop is possible if position is greater than loopEnd.
622 mPosLoop.mLoopCount = 0;
623 }
Andy Hung9b461582014-12-01 17:56:29 -0800624 (void) mMutator.push(mState);
625}
626
627void StaticAudioTrackClientProxy::setBufferPositionAndLoop(size_t position, size_t loopStart,
628 size_t loopEnd, int loopCount)
629{
630 setLoop(loopStart, loopEnd, loopCount);
631 setBufferPosition(position);
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800632}
633
634size_t StaticAudioTrackClientProxy::getBufferPosition()
635{
Andy Hung4ede21d2014-12-12 15:37:34 -0800636 getBufferPositionAndLoopCount(NULL, NULL);
637 return mPosLoop.mBufferPosition;
638}
639
640void StaticAudioTrackClientProxy::getBufferPositionAndLoopCount(
641 size_t *position, int *loopCount)
642{
643 if (mMutator.ack() == StaticAudioTrackSingleStateQueue::SSQ_DONE) {
644 if (mPosLoopObserver.poll(mPosLoop)) {
645 ; // a valid mPosLoop should be available if ackDone is true.
646 }
Glenn Kastena8190fc2012-12-03 17:06:56 -0800647 }
Andy Hung4ede21d2014-12-12 15:37:34 -0800648 if (position != NULL) {
649 *position = mPosLoop.mBufferPosition;
650 }
651 if (loopCount != NULL) {
652 *loopCount = mPosLoop.mLoopCount;
653 }
Glenn Kastena8190fc2012-12-03 17:06:56 -0800654}
655
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800656// ---------------------------------------------------------------------------
657
658ServerProxy::ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
659 size_t frameSize, bool isOut, bool clientInServer)
Glenn Kasten7db7df02013-06-25 16:13:23 -0700660 : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer),
Andy Hungea2b9c02016-02-12 17:06:53 -0800661 mAvailToClient(0), mFlush(0), mReleased(0), mFlushed(0)
Andy Hung6ae58432016-02-16 18:32:24 -0800662 , mTimestampMutator(&cblk->mExtendedTimestampQueue)
Glenn Kastena8190fc2012-12-03 17:06:56 -0800663{
Phil Burke8972b02016-03-04 11:29:57 -0800664 cblk->mBufferSizeInFrames = frameCount;
Glenn Kastena8190fc2012-12-03 17:06:56 -0800665}
666
ilewis926b82f2016-03-29 14:50:36 -0700667__attribute__((no_sanitize("integer")))
Phil Burk4bb650b2016-09-09 12:11:17 -0700668void ServerProxy::flushBufferIfNeeded()
669{
670 audio_track_cblk_t* cblk = mCblk;
671 // The acquire_load is not really required. But since the write is a release_store in the
672 // client, using acquire_load here makes it easier for people to maintain the code,
673 // and the logic for communicating ipc variables seems somewhat standard,
674 // and there really isn't much penalty for 4 or 8 byte atomics.
675 int32_t flush = android_atomic_acquire_load(&cblk->u.mStreaming.mFlush);
676 if (flush != mFlush) {
677 ALOGV("ServerProxy::flushBufferIfNeeded() mStreaming.mFlush = 0x%x, mFlush = 0x%0x",
678 flush, mFlush);
Andy Hung1d3556d2018-03-29 16:30:14 -0700679 // shouldn't matter, but for range safety use mRear instead of getRear().
Phil Burk4bb650b2016-09-09 12:11:17 -0700680 int32_t rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
681 int32_t front = cblk->u.mStreaming.mFront;
682
683 // effectively obtain then release whatever is in the buffer
684 const size_t overflowBit = mFrameCountP2 << 1;
685 const size_t mask = overflowBit - 1;
686 int32_t newFront = (front & ~mask) | (flush & mask);
687 ssize_t filled = rear - newFront;
688 if (filled >= (ssize_t)overflowBit) {
689 // front and rear offsets span the overflow bit of the p2 mask
690 // so rebasing newFront on the front offset is off by the overflow bit.
691 // adjust newFront to match rear offset.
692 ALOGV("flush wrap: filled %zx >= overflowBit %zx", filled, overflowBit);
693 newFront += overflowBit;
694 filled -= overflowBit;
695 }
696 // Rather than shutting down on a corrupt flush, just treat it as a full flush
697 if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
698 ALOGE("mFlush %#x -> %#x, front %#x, rear %#x, mask %#x, newFront %#x, "
699 "filled %zd=%#x",
700 mFlush, flush, front, rear,
701 (unsigned)mask, newFront, filled, (unsigned)filled);
702 newFront = rear;
703 }
704 mFlush = flush;
705 android_atomic_release_store(newFront, &cblk->u.mStreaming.mFront);
706 // There is no danger from a false positive, so err on the side of caution
707 if (true /*front != newFront*/) {
708 int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
709 if (!(old & CBLK_FUTEX_WAKE)) {
710 (void) syscall(__NR_futex, &cblk->mFutex,
711 mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
712 }
713 }
714 mFlushed += (newFront - front) & mask;
715 }
716}
717
718__attribute__((no_sanitize("integer")))
Andy Hung1d3556d2018-03-29 16:30:14 -0700719int32_t AudioTrackServerProxy::getRear() const
720{
721 const int32_t stop = android_atomic_acquire_load(&mCblk->u.mStreaming.mStop);
722 const int32_t rear = android_atomic_acquire_load(&mCblk->u.mStreaming.mRear);
723 const int32_t stopLast = mStopLast.load(std::memory_order_acquire);
724 if (stop != stopLast) {
725 const int32_t front = mCblk->u.mStreaming.mFront;
726 const size_t overflowBit = mFrameCountP2 << 1;
727 const size_t mask = overflowBit - 1;
728 int32_t newRear = (rear & ~mask) | (stop & mask);
729 ssize_t filled = newRear - front;
Andy Hung54274032018-04-19 18:16:44 -0700730 // overflowBit is unsigned, so cast to signed for comparison.
731 if (filled >= (ssize_t)overflowBit) {
Andy Hung1d3556d2018-03-29 16:30:14 -0700732 // front and rear offsets span the overflow bit of the p2 mask
Andy Hung54274032018-04-19 18:16:44 -0700733 // so rebasing newRear on the rear offset is off by the overflow bit.
Andy Hung1d3556d2018-03-29 16:30:14 -0700734 ALOGV("stop wrap: filled %zx >= overflowBit %zx", filled, overflowBit);
Andy Hung54274032018-04-19 18:16:44 -0700735 newRear -= overflowBit;
736 filled -= overflowBit;
Andy Hung1d3556d2018-03-29 16:30:14 -0700737 }
738 if (0 <= filled && (size_t) filled <= mFrameCount) {
739 // we're stopped, return the stop level as newRear
740 return newRear;
741 }
742
743 // A corrupt stop. Log error and ignore.
744 ALOGE("mStopLast %#x -> stop %#x, front %#x, rear %#x, mask %#x, newRear %#x, "
745 "filled %zd=%#x",
746 stopLast, stop, front, rear,
747 (unsigned)mask, newRear, filled, (unsigned)filled);
748 // Don't reset mStopLast as this is const.
749 }
750 return rear;
751}
752
753void AudioTrackServerProxy::start()
754{
755 mStopLast = android_atomic_acquire_load(&mCblk->u.mStreaming.mStop);
756}
757
758__attribute__((no_sanitize("integer")))
Glenn Kasten2e422c42013-10-18 13:00:29 -0700759status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush)
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800760{
Andy Hung9c64f342017-08-02 18:10:00 -0700761 LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0,
762 "%s: null or zero frame buffer, buffer:%p", __func__, buffer);
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800763 if (mIsShutdown) {
Glenn Kasten7db7df02013-06-25 16:13:23 -0700764 goto no_init;
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800765 }
Glenn Kasten7db7df02013-06-25 16:13:23 -0700766 {
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800767 audio_track_cblk_t* cblk = mCblk;
768 // compute number of frames available to write (AudioTrack) or read (AudioRecord),
769 // or use previous cached value from framesReady(), with added barrier if it omits.
770 int32_t front;
771 int32_t rear;
772 // See notes on barriers at ClientProxy::obtainBuffer()
773 if (mIsOut) {
Phil Burk4bb650b2016-09-09 12:11:17 -0700774 flushBufferIfNeeded(); // might modify mFront
Andy Hung1d3556d2018-03-29 16:30:14 -0700775 rear = getRear();
Richard Fitzgeraldb1a270d2013-05-14 12:12:21 +0100776 front = cblk->u.mStreaming.mFront;
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800777 } else {
778 front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
779 rear = cblk->u.mStreaming.mRear;
780 }
781 ssize_t filled = rear - front;
782 // pipe should not already be overfull
783 if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
Glenn Kasten1bfe09a2017-02-21 13:05:56 -0800784 ALOGE("Shared memory control block is corrupt (filled=%zd, mFrameCount=%zu); shutting down",
785 filled, mFrameCount);
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800786 mIsShutdown = true;
787 }
788 if (mIsShutdown) {
Glenn Kasten7db7df02013-06-25 16:13:23 -0700789 goto no_init;
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800790 }
791 // don't allow filling pipe beyond the nominal size
792 size_t availToServer;
793 if (mIsOut) {
794 availToServer = filled;
795 mAvailToClient = mFrameCount - filled;
796 } else {
797 availToServer = mFrameCount - filled;
798 mAvailToClient = filled;
799 }
800 // 'availToServer' may be non-contiguous, so return only the first contiguous chunk
801 size_t part1;
802 if (mIsOut) {
803 front &= mFrameCountP2 - 1;
804 part1 = mFrameCountP2 - front;
805 } else {
806 rear &= mFrameCountP2 - 1;
807 part1 = mFrameCountP2 - rear;
808 }
809 if (part1 > availToServer) {
810 part1 = availToServer;
811 }
812 size_t ask = buffer->mFrameCount;
813 if (part1 > ask) {
814 part1 = ask;
815 }
816 // is assignment redundant in some cases?
817 buffer->mFrameCount = part1;
818 buffer->mRaw = part1 > 0 ?
819 &((char *) mBuffers)[(mIsOut ? front : rear) * mFrameSize] : NULL;
820 buffer->mNonContig = availToServer - part1;
Glenn Kasten2e422c42013-10-18 13:00:29 -0700821 // After flush(), allow releaseBuffer() on a previously obtained buffer;
822 // see "Acknowledge any pending flush()" in audioflinger/Tracks.cpp.
823 if (!ackFlush) {
824 mUnreleased = part1;
825 }
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800826 return part1 > 0 ? NO_ERROR : WOULD_BLOCK;
Glenn Kasten7db7df02013-06-25 16:13:23 -0700827 }
828no_init:
829 buffer->mFrameCount = 0;
830 buffer->mRaw = NULL;
831 buffer->mNonContig = 0;
832 mUnreleased = 0;
833 return NO_INIT;
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800834}
835
ilewis926b82f2016-03-29 14:50:36 -0700836__attribute__((no_sanitize("integer")))
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800837void ServerProxy::releaseBuffer(Buffer* buffer)
838{
Glenn Kasten7db7df02013-06-25 16:13:23 -0700839 LOG_ALWAYS_FATAL_IF(buffer == NULL);
840 size_t stepCount = buffer->mFrameCount;
841 if (stepCount == 0 || mIsShutdown) {
842 // prevent accidental re-use of buffer
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800843 buffer->mFrameCount = 0;
844 buffer->mRaw = NULL;
845 buffer->mNonContig = 0;
846 return;
847 }
Andy Hung9c64f342017-08-02 18:10:00 -0700848 LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased && mUnreleased <= mFrameCount),
849 "%s: mUnreleased out of range, "
850 "!(stepCount:%zu <= mUnreleased:%zu <= mFrameCount:%zu)",
851 __func__, stepCount, mUnreleased, mFrameCount);
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800852 mUnreleased -= stepCount;
853 audio_track_cblk_t* cblk = mCblk;
854 if (mIsOut) {
855 int32_t front = cblk->u.mStreaming.mFront;
856 android_atomic_release_store(stepCount + front, &cblk->u.mStreaming.mFront);
857 } else {
858 int32_t rear = cblk->u.mStreaming.mRear;
859 android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear);
860 }
861
Glenn Kasten844f88c2014-05-09 13:38:09 -0700862 cblk->mServer += stepCount;
Andy Hung3f0c9022016-01-15 17:49:46 -0800863 mReleased += stepCount;
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800864
865 size_t half = mFrameCount / 2;
866 if (half == 0) {
867 half = 1;
868 }
Glenn Kastenfdac7c02014-01-28 11:03:28 -0800869 size_t minimum = (size_t) cblk->mMinimum;
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800870 if (minimum == 0) {
871 minimum = mIsOut ? half : 1;
872 } else if (minimum > half) {
873 minimum = half;
874 }
Glenn Kasten93bb77d2013-06-24 12:10:45 -0700875 // FIXME AudioRecord wakeup needs to be optimized; it currently wakes up client every time
Glenn Kastence8828a2013-09-16 18:07:38 -0700876 if (!mIsOut || (mAvailToClient + stepCount >= minimum)) {
Mark Salyzyn34fb2962014-06-18 16:30:56 -0700877 ALOGV("mAvailToClient=%zu stepCount=%zu minimum=%zu", mAvailToClient, stepCount, minimum);
Glenn Kasten0d09a9b2013-06-24 12:06:46 -0700878 int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
879 if (!(old & CBLK_FUTEX_WAKE)) {
Elliott Hughesee499292014-05-21 17:55:51 -0700880 (void) syscall(__NR_futex, &cblk->mFutex,
881 mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800882 }
883 }
884
885 buffer->mFrameCount = 0;
886 buffer->mRaw = NULL;
887 buffer->mNonContig = 0;
888}
889
890// ---------------------------------------------------------------------------
891
ilewis926b82f2016-03-29 14:50:36 -0700892__attribute__((no_sanitize("integer")))
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800893size_t AudioTrackServerProxy::framesReady()
894{
895 LOG_ALWAYS_FATAL_IF(!mIsOut);
896
897 if (mIsShutdown) {
898 return 0;
899 }
900 audio_track_cblk_t* cblk = mCblk;
Richard Fitzgeraldb1a270d2013-05-14 12:12:21 +0100901
902 int32_t flush = cblk->u.mStreaming.mFlush;
903 if (flush != mFlush) {
Glenn Kasten20f51b12014-10-30 10:43:19 -0700904 // FIXME should return an accurate value, but over-estimate is better than under-estimate
Richard Fitzgeraldb1a270d2013-05-14 12:12:21 +0100905 return mFrameCount;
906 }
Andy Hung1d3556d2018-03-29 16:30:14 -0700907 const int32_t rear = getRear();
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800908 ssize_t filled = rear - cblk->u.mStreaming.mFront;
909 // pipe should not already be overfull
910 if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
Glenn Kasten1bfe09a2017-02-21 13:05:56 -0800911 ALOGE("Shared memory control block is corrupt (filled=%zd, mFrameCount=%zu); shutting down",
912 filled, mFrameCount);
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800913 mIsShutdown = true;
914 return 0;
915 }
916 // cache this value for later use by obtainBuffer(), with added barrier
917 // and racy if called by normal mixer thread
918 // ignores flush(), so framesReady() may report a larger mFrameCount than obtainBuffer()
919 return filled;
920}
921
Andy Hung2c6c3bb2017-06-16 14:01:45 -0700922__attribute__((no_sanitize("integer")))
923size_t AudioTrackServerProxy::framesReadySafe() const
924{
925 if (mIsShutdown) {
926 return 0;
927 }
928 const audio_track_cblk_t* cblk = mCblk;
929 const int32_t flush = android_atomic_acquire_load(&cblk->u.mStreaming.mFlush);
930 if (flush != mFlush) {
931 return mFrameCount;
932 }
Andy Hung1d3556d2018-03-29 16:30:14 -0700933 const int32_t rear = getRear();
Andy Hung2c6c3bb2017-06-16 14:01:45 -0700934 const ssize_t filled = rear - cblk->u.mStreaming.mFront;
935 if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
936 return 0; // error condition, silently return 0.
937 }
938 return filled;
939}
940
Eric Laurentbfb1b832013-01-07 09:53:42 -0800941bool AudioTrackServerProxy::setStreamEndDone() {
Glenn Kasten844f88c2014-05-09 13:38:09 -0700942 audio_track_cblk_t* cblk = mCblk;
Eric Laurentbfb1b832013-01-07 09:53:42 -0800943 bool old =
Glenn Kasten844f88c2014-05-09 13:38:09 -0700944 (android_atomic_or(CBLK_STREAM_END_DONE, &cblk->mFlags) & CBLK_STREAM_END_DONE) != 0;
Eric Laurentbfb1b832013-01-07 09:53:42 -0800945 if (!old) {
Elliott Hughese348c5b2014-05-21 18:47:50 -0700946 (void) syscall(__NR_futex, &cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
Elliott Hughesee499292014-05-21 17:55:51 -0700947 1);
Eric Laurentbfb1b832013-01-07 09:53:42 -0800948 }
949 return old;
950}
951
Andy Hungd4ee4db2017-07-12 15:26:04 -0700952__attribute__((no_sanitize("integer")))
Glenn Kasten82aaf942013-07-17 16:05:07 -0700953void AudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount)
954{
Glenn Kasten844f88c2014-05-09 13:38:09 -0700955 audio_track_cblk_t* cblk = mCblk;
Phil Burk2812d9e2016-01-04 10:34:30 -0800956 if (frameCount > 0) {
957 cblk->u.mStreaming.mUnderrunFrames += frameCount;
Glenn Kasten82aaf942013-07-17 16:05:07 -0700958
Phil Burk2812d9e2016-01-04 10:34:30 -0800959 if (!mUnderrunning) { // start of underrun?
960 mUnderrunCount++;
961 cblk->u.mStreaming.mUnderrunCount = mUnderrunCount;
962 mUnderrunning = true;
963 ALOGV("tallyUnderrunFrames(%3u) at uf = %u, bump mUnderrunCount = %u",
964 frameCount, cblk->u.mStreaming.mUnderrunFrames, mUnderrunCount);
965 }
966
967 // FIXME also wake futex so that underrun is noticed more quickly
968 (void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags);
969 } else {
970 ALOGV_IF(mUnderrunning,
971 "tallyUnderrunFrames(%3u) at uf = %u, underrun finished",
972 frameCount, cblk->u.mStreaming.mUnderrunFrames);
973 mUnderrunning = false; // so we can detect the next edge
974 }
Glenn Kasten82aaf942013-07-17 16:05:07 -0700975}
976
Ricardo Garcia5a8a95d2015-04-18 14:47:04 -0700977AudioPlaybackRate AudioTrackServerProxy::getPlaybackRate()
Andy Hung8edb8dc2015-03-26 19:13:55 -0700978{ // do not call from multiple threads without holding lock
Ricardo Garcia5a8a95d2015-04-18 14:47:04 -0700979 mPlaybackRateObserver.poll(mPlaybackRate);
980 return mPlaybackRate;
Andy Hung8edb8dc2015-03-26 19:13:55 -0700981}
982
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800983// ---------------------------------------------------------------------------
984
985StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers,
986 size_t frameCount, size_t frameSize)
987 : AudioTrackServerProxy(cblk, buffers, frameCount, frameSize),
Andy Hung4ede21d2014-12-12 15:37:34 -0800988 mObserver(&cblk->u.mStatic.mSingleStateQueue),
989 mPosLoopMutator(&cblk->u.mStatic.mPosLoopQueue),
Andy Hungcb2129b2014-11-11 12:17:22 -0800990 mFramesReadySafe(frameCount), mFramesReady(frameCount),
991 mFramesReadyIsCalledByMultipleThreads(false)
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800992{
Andy Hung9b461582014-12-01 17:56:29 -0800993 memset(&mState, 0, sizeof(mState));
Glenn Kasten9f80dd22012-12-18 15:57:32 -0800994}
995
996void StaticAudioTrackServerProxy::framesReadyIsCalledByMultipleThreads()
997{
998 mFramesReadyIsCalledByMultipleThreads = true;
999}
1000
1001size_t StaticAudioTrackServerProxy::framesReady()
1002{
Andy Hungcb2129b2014-11-11 12:17:22 -08001003 // Can't call pollPosition() from multiple threads.
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001004 if (!mFramesReadyIsCalledByMultipleThreads) {
Andy Hungcb2129b2014-11-11 12:17:22 -08001005 (void) pollPosition();
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001006 }
Andy Hungcb2129b2014-11-11 12:17:22 -08001007 return mFramesReadySafe;
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001008}
1009
Andy Hung2c6c3bb2017-06-16 14:01:45 -07001010size_t StaticAudioTrackServerProxy::framesReadySafe() const
1011{
1012 return mFramesReadySafe;
1013}
1014
Andy Hung9b461582014-12-01 17:56:29 -08001015status_t StaticAudioTrackServerProxy::updateStateWithLoop(
1016 StaticAudioTrackState *localState, const StaticAudioTrackState &update) const
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001017{
Andy Hung9b461582014-12-01 17:56:29 -08001018 if (localState->mLoopSequence != update.mLoopSequence) {
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001019 bool valid = false;
Andy Hung9b461582014-12-01 17:56:29 -08001020 const size_t loopStart = update.mLoopStart;
1021 const size_t loopEnd = update.mLoopEnd;
1022 size_t position = localState->mPosition;
1023 if (update.mLoopCount == 0) {
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001024 valid = true;
Andy Hung9b461582014-12-01 17:56:29 -08001025 } else if (update.mLoopCount >= -1) {
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001026 if (loopStart < loopEnd && loopEnd <= mFrameCount &&
1027 loopEnd - loopStart >= MIN_LOOP) {
Andy Hung680b7952014-11-12 13:18:52 -08001028 // If the current position is greater than the end of the loop
1029 // we "wrap" to the loop start. This might cause an audible pop.
1030 if (position >= loopEnd) {
Andy Hung9b461582014-12-01 17:56:29 -08001031 position = loopStart;
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001032 }
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001033 valid = true;
1034 }
1035 }
Andy Hung9b461582014-12-01 17:56:29 -08001036 if (!valid || position > mFrameCount) {
1037 return NO_INIT;
1038 }
1039 localState->mPosition = position;
1040 localState->mLoopCount = update.mLoopCount;
1041 localState->mLoopEnd = loopEnd;
1042 localState->mLoopStart = loopStart;
1043 localState->mLoopSequence = update.mLoopSequence;
1044 }
1045 return OK;
1046}
1047
1048status_t StaticAudioTrackServerProxy::updateStateWithPosition(
1049 StaticAudioTrackState *localState, const StaticAudioTrackState &update) const
1050{
1051 if (localState->mPositionSequence != update.mPositionSequence) {
1052 if (update.mPosition > mFrameCount) {
1053 return NO_INIT;
1054 } else if (localState->mLoopCount != 0 && update.mPosition >= localState->mLoopEnd) {
1055 localState->mLoopCount = 0; // disable loop count if position is beyond loop end.
1056 }
1057 localState->mPosition = update.mPosition;
1058 localState->mPositionSequence = update.mPositionSequence;
1059 }
1060 return OK;
1061}
1062
1063ssize_t StaticAudioTrackServerProxy::pollPosition()
1064{
1065 StaticAudioTrackState state;
1066 if (mObserver.poll(state)) {
1067 StaticAudioTrackState trystate = mState;
1068 bool result;
Chad Brubakercb50c542015-10-07 14:20:10 -07001069 const int32_t diffSeq = (int32_t) state.mLoopSequence - (int32_t) state.mPositionSequence;
Andy Hung9b461582014-12-01 17:56:29 -08001070
1071 if (diffSeq < 0) {
1072 result = updateStateWithLoop(&trystate, state) == OK &&
1073 updateStateWithPosition(&trystate, state) == OK;
1074 } else {
1075 result = updateStateWithPosition(&trystate, state) == OK &&
1076 updateStateWithLoop(&trystate, state) == OK;
1077 }
1078 if (!result) {
Andy Hung4ede21d2014-12-12 15:37:34 -08001079 mObserver.done();
Andy Hung9b461582014-12-01 17:56:29 -08001080 // caution: no update occurs so server state will be inconsistent with client state.
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001081 ALOGE("%s client pushed an invalid state, shutting down", __func__);
1082 mIsShutdown = true;
1083 return (ssize_t) NO_INIT;
1084 }
Andy Hung9b461582014-12-01 17:56:29 -08001085 mState = trystate;
1086 if (mState.mLoopCount == -1) {
1087 mFramesReady = INT64_MAX;
1088 } else if (mState.mLoopCount == 0) {
1089 mFramesReady = mFrameCount - mState.mPosition;
1090 } else if (mState.mLoopCount > 0) {
1091 // TODO: Later consider fixing overflow, but does not seem needed now
1092 // as will not overflow if loopStart and loopEnd are Java "ints".
1093 mFramesReady = int64_t(mState.mLoopCount) * (mState.mLoopEnd - mState.mLoopStart)
1094 + mFrameCount - mState.mPosition;
1095 }
Andy Hungcb2129b2014-11-11 12:17:22 -08001096 mFramesReadySafe = clampToSize(mFramesReady);
Glenn Kastenfdac7c02014-01-28 11:03:28 -08001097 // This may overflow, but client is not supposed to rely on it
Andy Hung4ede21d2014-12-12 15:37:34 -08001098 StaticAudioTrackPosLoop posLoop;
1099
1100 posLoop.mLoopCount = (int32_t) mState.mLoopCount;
1101 posLoop.mBufferPosition = (uint32_t) mState.mPosition;
1102 mPosLoopMutator.push(posLoop);
1103 mObserver.done(); // safe to read mStatic variables.
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001104 }
Andy Hung9b461582014-12-01 17:56:29 -08001105 return (ssize_t) mState.mPosition;
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001106}
1107
Andy Hungd4ee4db2017-07-12 15:26:04 -07001108__attribute__((no_sanitize("integer")))
Andy Hung954ca452015-09-09 14:39:02 -07001109status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush)
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001110{
1111 if (mIsShutdown) {
1112 buffer->mFrameCount = 0;
1113 buffer->mRaw = NULL;
1114 buffer->mNonContig = 0;
1115 mUnreleased = 0;
1116 return NO_INIT;
1117 }
1118 ssize_t positionOrStatus = pollPosition();
1119 if (positionOrStatus < 0) {
1120 buffer->mFrameCount = 0;
1121 buffer->mRaw = NULL;
1122 buffer->mNonContig = 0;
1123 mUnreleased = 0;
1124 return (status_t) positionOrStatus;
1125 }
1126 size_t position = (size_t) positionOrStatus;
Andy Hungcb2129b2014-11-11 12:17:22 -08001127 size_t end = mState.mLoopCount != 0 ? mState.mLoopEnd : mFrameCount;
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001128 size_t avail;
Andy Hungcb2129b2014-11-11 12:17:22 -08001129 if (position < end) {
1130 avail = end - position;
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001131 size_t wanted = buffer->mFrameCount;
1132 if (avail < wanted) {
1133 buffer->mFrameCount = avail;
1134 } else {
1135 avail = wanted;
1136 }
1137 buffer->mRaw = &((char *) mBuffers)[position * mFrameSize];
1138 } else {
1139 avail = 0;
1140 buffer->mFrameCount = 0;
1141 buffer->mRaw = NULL;
1142 }
Andy Hungcb2129b2014-11-11 12:17:22 -08001143 // As mFramesReady is the total remaining frames in the static audio track,
1144 // it is always larger or equal to avail.
Andy Hung9c64f342017-08-02 18:10:00 -07001145 LOG_ALWAYS_FATAL_IF(mFramesReady < (int64_t) avail,
1146 "%s: mFramesReady out of range, mFramesReady:%lld < avail:%zu",
1147 __func__, (long long)mFramesReady, avail);
Andy Hungcb2129b2014-11-11 12:17:22 -08001148 buffer->mNonContig = mFramesReady == INT64_MAX ? SIZE_MAX : clampToSize(mFramesReady - avail);
Andy Hung954ca452015-09-09 14:39:02 -07001149 if (!ackFlush) {
1150 mUnreleased = avail;
1151 }
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001152 return NO_ERROR;
1153}
1154
Andy Hungd4ee4db2017-07-12 15:26:04 -07001155__attribute__((no_sanitize("integer")))
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001156void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer)
1157{
1158 size_t stepCount = buffer->mFrameCount;
Andy Hung9c64f342017-08-02 18:10:00 -07001159 LOG_ALWAYS_FATAL_IF(!((int64_t) stepCount <= mFramesReady),
1160 "%s: stepCount out of range, "
1161 "!(stepCount:%zu <= mFramesReady:%lld)",
1162 __func__, stepCount, (long long)mFramesReady);
1163 LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased),
1164 "%s: stepCount out of range, "
1165 "!(stepCount:%zu <= mUnreleased:%zu)",
1166 __func__, stepCount, mUnreleased);
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001167 if (stepCount == 0) {
Glenn Kasten7db7df02013-06-25 16:13:23 -07001168 // prevent accidental re-use of buffer
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001169 buffer->mRaw = NULL;
1170 buffer->mNonContig = 0;
1171 return;
1172 }
1173 mUnreleased -= stepCount;
1174 audio_track_cblk_t* cblk = mCblk;
Andy Hung9b461582014-12-01 17:56:29 -08001175 size_t position = mState.mPosition;
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001176 size_t newPosition = position + stepCount;
1177 int32_t setFlags = 0;
1178 if (!(position <= newPosition && newPosition <= mFrameCount)) {
Glenn Kastenb187de12014-12-30 08:18:15 -08001179 ALOGW("%s newPosition %zu outside [%zu, %zu]", __func__, newPosition, position,
1180 mFrameCount);
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001181 newPosition = mFrameCount;
1182 } else if (mState.mLoopCount != 0 && newPosition == mState.mLoopEnd) {
Andy Hungcb2129b2014-11-11 12:17:22 -08001183 newPosition = mState.mLoopStart;
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001184 if (mState.mLoopCount == -1 || --mState.mLoopCount != 0) {
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001185 setFlags = CBLK_LOOP_CYCLE;
1186 } else {
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001187 setFlags = CBLK_LOOP_FINAL;
1188 }
1189 }
1190 if (newPosition == mFrameCount) {
1191 setFlags |= CBLK_BUFFER_END;
1192 }
Andy Hung9b461582014-12-01 17:56:29 -08001193 mState.mPosition = newPosition;
Andy Hungcb2129b2014-11-11 12:17:22 -08001194 if (mFramesReady != INT64_MAX) {
1195 mFramesReady -= stepCount;
1196 }
1197 mFramesReadySafe = clampToSize(mFramesReady);
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001198
Glenn Kastenf20e1d82013-07-12 09:45:18 -07001199 cblk->mServer += stepCount;
Andy Hung3f0c9022016-01-15 17:49:46 -08001200 mReleased += stepCount;
1201
Glenn Kastenfdac7c02014-01-28 11:03:28 -08001202 // This may overflow, but client is not supposed to rely on it
Andy Hung4ede21d2014-12-12 15:37:34 -08001203 StaticAudioTrackPosLoop posLoop;
1204 posLoop.mBufferPosition = mState.mPosition;
1205 posLoop.mLoopCount = mState.mLoopCount;
1206 mPosLoopMutator.push(posLoop);
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001207 if (setFlags != 0) {
Glenn Kasten96f60d82013-07-12 10:21:18 -07001208 (void) android_atomic_or(setFlags, &cblk->mFlags);
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001209 // this would be a good place to wake a futex
1210 }
1211
1212 buffer->mFrameCount = 0;
1213 buffer->mRaw = NULL;
1214 buffer->mNonContig = 0;
1215}
1216
Phil Burk2812d9e2016-01-04 10:34:30 -08001217void StaticAudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount)
Glenn Kasten82aaf942013-07-17 16:05:07 -07001218{
1219 // Unlike AudioTrackServerProxy::tallyUnderrunFrames() used for streaming tracks,
1220 // we don't have a location to count underrun frames. The underrun frame counter
1221 // only exists in AudioTrackSharedStreaming. Fortunately, underruns are not
1222 // possible for static buffer tracks other than at end of buffer, so this is not a loss.
1223
1224 // FIXME also wake futex so that underrun is noticed more quickly
Phil Burk2812d9e2016-01-04 10:34:30 -08001225 if (frameCount > 0) {
1226 (void) android_atomic_or(CBLK_UNDERRUN, &mCblk->mFlags);
1227 }
Glenn Kasten82aaf942013-07-17 16:05:07 -07001228}
1229
Andy Hung1d3556d2018-03-29 16:30:14 -07001230int32_t StaticAudioTrackServerProxy::getRear() const
1231{
1232 LOG_ALWAYS_FATAL("getRear() not permitted for static tracks");
1233 return 0;
1234}
1235
Glenn Kasten9f80dd22012-12-18 15:57:32 -08001236// ---------------------------------------------------------------------------
1237
Glenn Kastena8190fc2012-12-03 17:06:56 -08001238} // namespace android