1 /*
2 * Copyright (C) 2012 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 // <IMPORTANT_WARNING>
18 // Design rules for threadLoop() are given in the comments at section "Fast mixer thread" of
19 // StateQueue.h. In particular, avoid library and system calls except at well-known points.
20 // The design rules are only for threadLoop(), and don't apply to FastMixerDumpState methods.
21 // </IMPORTANT_WARNING>
22
23 #define LOG_TAG "FastMixer"
24 //#define LOG_NDEBUG 0
25
26 #define ATRACE_TAG ATRACE_TAG_AUDIO
27
28 #include "Configuration.h"
29 #include <time.h>
30 #include <utils/Log.h>
31 #include <utils/Trace.h>
32 #include <system/audio.h>
33 #ifdef FAST_THREAD_STATISTICS
34 #include <audio_utils/Statistics.h>
35 #ifdef CPU_FREQUENCY_STATISTICS
36 #include <cpustats/ThreadCpuUsage.h>
37 #endif
38 #endif
39 #include <audio_utils/channels.h>
40 #include <audio_utils/format.h>
41 #include <audio_utils/mono_blend.h>
42 #include <cutils/bitops.h>
43 #include <media/AudioMixer.h>
44 #include "FastMixer.h"
45 #include <afutils/TypedLogger.h>
46
47 namespace android {
48
49 /*static*/ const FastMixerState FastMixer::sInitial;
50
getChannelMaskFromCount(size_t count)51 static audio_channel_mask_t getChannelMaskFromCount(size_t count) {
52 const audio_channel_mask_t mask = audio_channel_out_mask_from_count(count);
53 if (mask == AUDIO_CHANNEL_INVALID) {
54 // some counts have no positional masks. TODO: Update this to return index count?
55 return audio_channel_mask_for_index_assignment_from_count(count);
56 }
57 return mask;
58 }
59
FastMixer(audio_io_handle_t parentIoHandle)60 FastMixer::FastMixer(audio_io_handle_t parentIoHandle)
61 : FastThread("cycle_ms", "load_us"),
62 // mFastTrackNames
63 // mGenerations
64 // timestamp
65 mThreadIoHandle(parentIoHandle)
66 {
67 // FIXME pass sInitial as parameter to base class constructor, and make it static local
68 mPrevious = &sInitial;
69 mCurrent = &sInitial;
70 mDummyDumpState = &mDummyFastMixerDumpState;
71
72 // TODO: Add channel mask to NBAIO_Format.
73 // We assume that the channel mask must be a valid positional channel mask.
74 mSinkChannelMask = getChannelMaskFromCount(mSinkChannelCount);
75 mBalance.setChannelMask(mSinkChannelMask);
76
77 #ifdef FAST_THREAD_STATISTICS
78 mOldLoad.tv_sec = 0;
79 mOldLoad.tv_nsec = 0;
80 #endif
81 }
82
sq()83 FastMixerStateQueue* FastMixer::sq()
84 {
85 return &mSQ;
86 }
87
poll()88 const FastThreadState *FastMixer::poll()
89 {
90 return mSQ.poll();
91 }
92
setNBLogWriter(NBLog::Writer * logWriter __unused)93 void FastMixer::setNBLogWriter(NBLog::Writer *logWriter __unused)
94 {
95 }
96
onIdle()97 void FastMixer::onIdle()
98 {
99 mPreIdle = *(const FastMixerState *)mCurrent;
100 mCurrent = &mPreIdle;
101 }
102
onExit()103 void FastMixer::onExit()
104 {
105 delete mMixer;
106 free(mMixerBuffer);
107 free(mSinkBuffer);
108 }
109
isSubClassCommand(FastThreadState::Command command)110 bool FastMixer::isSubClassCommand(FastThreadState::Command command)
111 {
112 switch ((FastMixerState::Command) command) {
113 case FastMixerState::MIX:
114 case FastMixerState::WRITE:
115 case FastMixerState::MIX_WRITE:
116 return true;
117 default:
118 return false;
119 }
120 }
121
updateMixerTrack(int index,Reason reason)122 void FastMixer::updateMixerTrack(int index, Reason reason) {
123 const FastMixerState * const current = (const FastMixerState *) mCurrent;
124 const FastTrack * const fastTrack = ¤t->mFastTracks[index];
125
126 // check and update generation
127 if (reason == REASON_MODIFY && mGenerations[index] == fastTrack->mGeneration) {
128 return; // no change on an already configured track.
129 }
130 mGenerations[index] = fastTrack->mGeneration;
131
132 // mMixer == nullptr on configuration failure (check done after generation update).
133 if (mMixer == nullptr) {
134 return;
135 }
136
137 switch (reason) {
138 case REASON_REMOVE:
139 mMixer->destroy(index);
140 break;
141 case REASON_ADD: {
142 const status_t status = mMixer->create(
143 index, fastTrack->mChannelMask, fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
144 LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
145 "%s: cannot create fast track index"
146 " %d, mask %#x, format %#x in AudioMixer",
147 __func__, index, fastTrack->mChannelMask, fastTrack->mFormat);
148 }
149 [[fallthrough]]; // now fallthrough to update the newly created track.
150 case REASON_MODIFY:
151 mMixer->setBufferProvider(index, fastTrack->mBufferProvider);
152
153 float vlf, vrf;
154 if (fastTrack->mVolumeProvider != nullptr) {
155 const gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR();
156 vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
157 vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
158 } else {
159 vlf = vrf = AudioMixer::UNITY_GAIN_FLOAT;
160 }
161
162 // set volume to avoid ramp whenever the track is updated (or created).
163 // Note: this does not distinguish from starting fresh or
164 // resuming from a paused state.
165 mMixer->setParameter(index, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf);
166 mMixer->setParameter(index, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf);
167
168 mMixer->setParameter(index, AudioMixer::RESAMPLE, AudioMixer::REMOVE, nullptr);
169 mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
170 (void *)mMixerBuffer);
171 mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::MIXER_FORMAT,
172 (void *)(uintptr_t)mMixerBufferFormat);
173 mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::FORMAT,
174 (void *)(uintptr_t)fastTrack->mFormat);
175 mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
176 (void *)(uintptr_t)fastTrack->mChannelMask);
177 mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK,
178 (void *)(uintptr_t)mSinkChannelMask);
179 mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED,
180 (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled);
181 mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::HAPTIC_SCALE,
182 (void *)(&(fastTrack->mHapticScale)));
183 mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::HAPTIC_MAX_AMPLITUDE,
184 (void *)(&(fastTrack->mHapticMaxAmplitude)));
185
186 mMixer->enable(index);
187 break;
188 default:
189 LOG_ALWAYS_FATAL("%s: invalid update reason %d", __func__, reason);
190 }
191 }
192
onStateChange()193 void FastMixer::onStateChange()
194 {
195 const FastMixerState * const current = (const FastMixerState *) mCurrent;
196 const FastMixerState * const previous = (const FastMixerState *) mPrevious;
197 FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState;
198 const size_t frameCount = current->mFrameCount;
199
200 // update boottime offset, in case it has changed
201 mTimestamp.mTimebaseOffset[ExtendedTimestamp::TIMEBASE_BOOTTIME] =
202 mBoottimeOffset.load();
203
204 // handle state change here, but since we want to diff the state,
205 // we're prepared for previous == &sInitial the first time through
206 unsigned previousTrackMask;
207
208 // check for change in output HAL configuration
209 const NBAIO_Format previousFormat = mFormat;
210 if (current->mOutputSinkGen != mOutputSinkGen) {
211 mOutputSink = current->mOutputSink;
212 mOutputSinkGen = current->mOutputSinkGen;
213 mSinkChannelMask = current->mSinkChannelMask;
214 mBalance.setChannelMask(mSinkChannelMask);
215 if (mOutputSink == nullptr) {
216 mFormat = Format_Invalid;
217 mSampleRate = 0;
218 mSinkChannelCount = 0;
219 mSinkChannelMask = AUDIO_CHANNEL_NONE;
220 mAudioChannelCount = 0;
221 } else {
222 mFormat = mOutputSink->format();
223 mSampleRate = Format_sampleRate(mFormat);
224 mSinkChannelCount = Format_channelCount(mFormat);
225 LOG_ALWAYS_FATAL_IF(mSinkChannelCount > AudioMixer::MAX_NUM_CHANNELS);
226
227 if (mSinkChannelMask == AUDIO_CHANNEL_NONE) {
228 mSinkChannelMask = getChannelMaskFromCount(mSinkChannelCount);
229 }
230 mAudioChannelCount = mSinkChannelCount - audio_channel_count_from_out_mask(
231 mSinkChannelMask & AUDIO_CHANNEL_HAPTIC_ALL);
232 }
233 dumpState->mSampleRate = mSampleRate;
234 }
235
236 if ((!Format_isEqual(mFormat, previousFormat)) || (frameCount != previous->mFrameCount)) {
237 // FIXME to avoid priority inversion, don't delete here
238 delete mMixer;
239 mMixer = nullptr;
240 free(mMixerBuffer);
241 mMixerBuffer = nullptr;
242 free(mSinkBuffer);
243 mSinkBuffer = nullptr;
244 if (frameCount > 0 && mSampleRate > 0) {
245 // FIXME new may block for unbounded time at internal mutex of the heap
246 // implementation; it would be better to have normal mixer allocate for us
247 // to avoid blocking here and to prevent possible priority inversion
248 mMixer = new AudioMixer(frameCount, mSampleRate);
249 // FIXME See the other FIXME at FastMixer::setNBLogWriter()
250 NBLog::thread_params_t params;
251 params.frameCount = frameCount;
252 params.sampleRate = mSampleRate;
253 LOG_THREAD_PARAMS(params);
254 const size_t mixerFrameSize = mSinkChannelCount
255 * audio_bytes_per_sample(mMixerBufferFormat);
256 mMixerBufferSize = mixerFrameSize * frameCount;
257 (void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize);
258 const size_t sinkFrameSize = mSinkChannelCount
259 * audio_bytes_per_sample(mFormat.mFormat);
260 if (sinkFrameSize > mixerFrameSize) { // need a sink buffer
261 mSinkBufferSize = sinkFrameSize * frameCount;
262 (void)posix_memalign(&mSinkBuffer, 32, mSinkBufferSize);
263 }
264 mPeriodNs = (frameCount * 1000000000LL) / mSampleRate; // 1.00
265 mUnderrunNs = (frameCount * 1750000000LL) / mSampleRate; // 1.75
266 mOverrunNs = (frameCount * 500000000LL) / mSampleRate; // 0.50
267 mForceNs = (frameCount * 950000000LL) / mSampleRate; // 0.95
268 mWarmupNsMin = (frameCount * 750000000LL) / mSampleRate; // 0.75
269 mWarmupNsMax = (frameCount * 1250000000LL) / mSampleRate; // 1.25
270 } else {
271 mPeriodNs = 0;
272 mUnderrunNs = 0;
273 mOverrunNs = 0;
274 mForceNs = 0;
275 mWarmupNsMin = 0;
276 mWarmupNsMax = LONG_MAX;
277 }
278 mMixerBufferState = UNDEFINED;
279 // we need to reconfigure all active tracks
280 previousTrackMask = 0;
281 mFastTracksGen = current->mFastTracksGen - 1;
282 dumpState->mFrameCount = frameCount;
283 #ifdef TEE_SINK
284 mTee.set(mFormat, NBAIO_Tee::TEE_FLAG_OUTPUT_THREAD);
285 mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) + "_F");
286 #endif
287 } else {
288 previousTrackMask = previous->mTrackMask;
289 }
290
291 // check for change in active track set
292 const unsigned currentTrackMask = current->mTrackMask;
293 dumpState->mTrackMask = currentTrackMask;
294 dumpState->mNumTracks = popcount(currentTrackMask);
295 if (current->mFastTracksGen != mFastTracksGen) {
296
297 // process removed tracks first to avoid running out of track names
298 unsigned removedTracks = previousTrackMask & ~currentTrackMask;
299 while (removedTracks != 0) {
300 const int i = __builtin_ctz(removedTracks);
301 removedTracks &= ~(1 << i);
302 updateMixerTrack(i, REASON_REMOVE);
303 // don't reset track dump state, since other side is ignoring it
304 }
305
306 // now process added tracks
307 unsigned addedTracks = currentTrackMask & ~previousTrackMask;
308 while (addedTracks != 0) {
309 const int i = __builtin_ctz(addedTracks);
310 addedTracks &= ~(1 << i);
311 updateMixerTrack(i, REASON_ADD);
312 }
313
314 // finally process (potentially) modified tracks; these use the same slot
315 // but may have a different buffer provider or volume provider
316 unsigned modifiedTracks = currentTrackMask & previousTrackMask;
317 while (modifiedTracks != 0) {
318 const int i = __builtin_ctz(modifiedTracks);
319 modifiedTracks &= ~(1 << i);
320 updateMixerTrack(i, REASON_MODIFY);
321 }
322
323 mFastTracksGen = current->mFastTracksGen;
324 }
325 }
326
onWork()327 void FastMixer::onWork()
328 {
329 // TODO: pass an ID parameter to indicate which time series we want to write to in NBLog.cpp
330 // Or: pass both of these into a single call with a boolean
331 const FastMixerState * const current = (const FastMixerState *) mCurrent;
332 FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState;
333
334 if (mIsWarm) {
335 // Logging timestamps for FastMixer is currently disabled to make memory room for logging
336 // other statistics in FastMixer.
337 // To re-enable, delete the #ifdef FASTMIXER_LOG_HIST_TS lines (and the #endif lines).
338 #ifdef FASTMIXER_LOG_HIST_TS
339 LOG_HIST_TS();
340 #endif
341 //ALOGD("Eric FastMixer::onWork() mIsWarm");
342 } else {
343 dumpState->mTimestampVerifier.discontinuity(
344 dumpState->mTimestampVerifier.DISCONTINUITY_MODE_CONTINUOUS);
345 // See comment in if block.
346 #ifdef FASTMIXER_LOG_HIST_TS
347 LOG_AUDIO_STATE();
348 #endif
349 }
350 const FastMixerState::Command command = mCommand;
351 const size_t frameCount = current->mFrameCount;
352
353 if ((command & FastMixerState::MIX) && (mMixer != nullptr) && mIsWarm) {
354 ALOG_ASSERT(mMixerBuffer != nullptr);
355
356 // AudioMixer::mState.enabledTracks is undefined if mState.hook == process__validate,
357 // so we keep a side copy of enabledTracks
358 bool anyEnabledTracks = false;
359
360 // for each track, update volume and check for underrun
361 unsigned currentTrackMask = current->mTrackMask;
362 while (currentTrackMask != 0) {
363 const int i = __builtin_ctz(currentTrackMask);
364 currentTrackMask &= ~(1 << i);
365 const FastTrack* fastTrack = ¤t->mFastTracks[i];
366
367 const int64_t trackFramesWrittenButNotPresented =
368 mNativeFramesWrittenButNotPresented;
369 const int64_t trackFramesWritten = fastTrack->mBufferProvider->framesReleased();
370 ExtendedTimestamp perTrackTimestamp(mTimestamp);
371
372 // Can't provide an ExtendedTimestamp before first frame presented.
373 // Also, timestamp may not go to very last frame on stop().
374 if (trackFramesWritten >= trackFramesWrittenButNotPresented &&
375 perTrackTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] > 0) {
376 perTrackTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] =
377 trackFramesWritten - trackFramesWrittenButNotPresented;
378 } else {
379 perTrackTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = 0;
380 perTrackTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] = -1;
381 }
382 perTrackTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] = trackFramesWritten;
383 fastTrack->mBufferProvider->onTimestamp(perTrackTimestamp);
384
385 const int name = i;
386 if (fastTrack->mVolumeProvider != nullptr) {
387 const gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR();
388 float vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
389 float vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
390
391 mMixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME0, &vlf);
392 mMixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME1, &vrf);
393 }
394 // FIXME The current implementation of framesReady() for fast tracks
395 // takes a tryLock, which can block
396 // up to 1 ms. If enough active tracks all blocked in sequence, this would result
397 // in the overall fast mix cycle being delayed. Should use a non-blocking FIFO.
398 const size_t framesReady = fastTrack->mBufferProvider->framesReady();
399 if (ATRACE_ENABLED()) {
400 // I wish we had formatted trace names
401 char traceName[16];
402 strcpy(traceName, "fRdy");
403 traceName[4] = i + (i < 10 ? '0' : 'A' - 10);
404 traceName[5] = '\0';
405 ATRACE_INT(traceName, framesReady);
406 }
407 FastTrackDump *ftDump = &dumpState->mTracks[i];
408 FastTrackUnderruns underruns = ftDump->mUnderruns;
409 if (framesReady < frameCount) {
410 if (framesReady == 0) {
411 underruns.mBitFields.mEmpty++;
412 underruns.mBitFields.mMostRecent = UNDERRUN_EMPTY;
413 mMixer->disable(name);
414 } else {
415 // allow mixing partial buffer
416 underruns.mBitFields.mPartial++;
417 underruns.mBitFields.mMostRecent = UNDERRUN_PARTIAL;
418 mMixer->enable(name);
419 anyEnabledTracks = true;
420 }
421 } else {
422 underruns.mBitFields.mFull++;
423 underruns.mBitFields.mMostRecent = UNDERRUN_FULL;
424 mMixer->enable(name);
425 anyEnabledTracks = true;
426 }
427 ftDump->mUnderruns = underruns;
428 ftDump->mFramesReady = framesReady;
429 ftDump->mFramesWritten = trackFramesWritten;
430 }
431
432 if (anyEnabledTracks) {
433 // process() is CPU-bound
434 mMixer->process();
435 mMixerBufferState = MIXED;
436 } else if (mMixerBufferState != ZEROED) {
437 mMixerBufferState = UNDEFINED;
438 }
439
440 } else if (mMixerBufferState == MIXED) {
441 mMixerBufferState = UNDEFINED;
442 }
443 //bool didFullWrite = false; // dumpsys could display a count of partial writes
444 if ((command & FastMixerState::WRITE)
445 && (mOutputSink != nullptr) && (mMixerBuffer != nullptr)) {
446 if (mMixerBufferState == UNDEFINED) {
447 memset(mMixerBuffer, 0, mMixerBufferSize);
448 mMixerBufferState = ZEROED;
449 }
450
451 if (mMasterMono.load()) { // memory_order_seq_cst
452 mono_blend(mMixerBuffer, mMixerBufferFormat, Format_channelCount(mFormat), frameCount,
453 true /*limit*/);
454 }
455
456 // Balance must take effect after mono conversion.
457 // mBalance detects zero balance within the class for speed (not needed here).
458 mBalance.setBalance(mMasterBalance.load());
459 mBalance.process((float *)mMixerBuffer, frameCount);
460
461 // prepare the buffer used to write to sink
462 void *buffer = mSinkBuffer != nullptr ? mSinkBuffer : mMixerBuffer;
463 if (mFormat.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format
464 memcpy_by_audio_format(buffer, mFormat.mFormat, mMixerBuffer, mMixerBufferFormat,
465 frameCount * Format_channelCount(mFormat));
466 }
467 if (mSinkChannelMask & AUDIO_CHANNEL_HAPTIC_ALL) {
468 // When there are haptic channels, the sample data is partially interleaved.
469 // Make the sample data fully interleaved here.
470 adjust_channels_non_destructive(buffer, mAudioChannelCount, buffer, mSinkChannelCount,
471 audio_bytes_per_sample(mFormat.mFormat),
472 frameCount * audio_bytes_per_frame(mAudioChannelCount, mFormat.mFormat));
473 }
474 // if non-nullptr, then duplicate write() to this non-blocking sink
475 #ifdef TEE_SINK
476 mTee.write(buffer, frameCount);
477 #endif
478 // FIXME write() is non-blocking and lock-free for a properly implemented NBAIO sink,
479 // but this code should be modified to handle both non-blocking and blocking sinks
480 dumpState->mWriteSequence++;
481 ATRACE_BEGIN("write");
482 const ssize_t framesWritten = mOutputSink->write(buffer, frameCount);
483 ATRACE_END();
484 dumpState->mWriteSequence++;
485 if (framesWritten >= 0) {
486 ALOG_ASSERT((size_t) framesWritten <= frameCount);
487 mTotalNativeFramesWritten += framesWritten;
488 dumpState->mFramesWritten = mTotalNativeFramesWritten;
489 //if ((size_t) framesWritten == frameCount) {
490 // didFullWrite = true;
491 //}
492 } else {
493 dumpState->mWriteErrors++;
494 }
495 mAttemptedWrite = true;
496 // FIXME count # of writes blocked excessively, CPU usage, etc. for dump
497
498 if (mIsWarm) {
499 ExtendedTimestamp timestamp; // local
500 status_t status = mOutputSink->getTimestamp(timestamp);
501 if (status == NO_ERROR) {
502 dumpState->mTimestampVerifier.add(
503 timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL],
504 timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL],
505 mSampleRate);
506 const int64_t totalNativeFramesPresented =
507 timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
508 if (totalNativeFramesPresented <= mTotalNativeFramesWritten) {
509 mNativeFramesWrittenButNotPresented =
510 mTotalNativeFramesWritten - totalNativeFramesPresented;
511 mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] =
512 timestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
513 mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] =
514 timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL];
515 // We don't compensate for server - kernel time difference and
516 // only update latency if we have valid info.
517 const double latencyMs =
518 (double)mNativeFramesWrittenButNotPresented * 1000 / mSampleRate;
519 dumpState->mLatencyMs = latencyMs;
520 LOG_LATENCY(latencyMs);
521 } else {
522 // HAL reported that more frames were presented than were written
523 mNativeFramesWrittenButNotPresented = 0;
524 status = INVALID_OPERATION;
525 }
526 } else {
527 dumpState->mTimestampVerifier.error();
528 }
529 if (status == NO_ERROR) {
530 mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] =
531 mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL];
532 } else {
533 // fetch server time if we can't get timestamp
534 mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] =
535 systemTime(SYSTEM_TIME_MONOTONIC);
536 // clear out kernel cached position as this may get rapidly stale
537 // if we never get a new valid timestamp
538 mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL] = 0;
539 mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL] = -1;
540 }
541 }
542 }
543 }
544
545 } // namespace android
546