Skip to content

TechNote_HowToAvoidCrashes

Phil Burk edited this page Jun 13, 2024 · 1 revision

How to Avoid Crashes

Most of the crashes related to Oboe involve "use after free" bugs. These generally occur when a callback tries to reference memory that was freed by a foreground task.

Please take the following steps to avoid these types of errors:

Recommendations

Use Oboe v1.9.0 or later

Bug #2035 was fixed in Oboe V1.9.0.

Avoid Deleting Objects that are Used by Callbacks

If you have an object that is used by the callbacks then you should not delete that object. I recommend allocating it statically as a single global object or allocating it inside a larger app structure that stays allocated.

Specifically, do not delete your audio related objects right when you are closing a stream. Sometimes callback threads, particularly error callbacks can run after the stream is closed. This can happen if an app is closing a stream at the same time that a user plugs in or unplugs a headset.

Use std::shared_ptr when Opening Streams

This will allow a shared_ptr to be passed to the callback functions. This can help prevent the object from being deleted while the callback is executing.

guide for openStream

Do not reset() that shared_ptr when you close the stream. Just keep the stream reference until you replace it with a new stream. Streams are not very big. It is better to use a little more memory than to risk a crash.

See bug #820.

Use std::shared_ptr or Never Delete Callback Objects

This applies to AudioStreamDataCallback and AudioStreamErrorCallback subclass objects. You should either statically allocate them as part of your statically allocated audio engine. Or store the reference in a shared_ptr and pass the shared_ptr when passing them to the Builder.

// Declare this in some permanent place that will not get deleted.    
std::shared_ptr<MyDataCallback> mSharedDataCallback;

// Call this before you setup the builder.
mSharedDataCallback = std::make_shared<MyDataCallback>();

builder.setDataCallback(mSharedDataCallback).

The same should also be done for error callbacks.

Avoid calling certain Methods from a Callback

These methods can trigger routing changes, which could cause problems on older versions of Android. See bugs below.

  • Do not call stream->getFramesRead() from inside the callback of an OUTPUT stream.
  • Do not call stream->getFramesWritten() from inside the callback of an INPUT stream.
  • Do not call stream->getTimestamp() from inside the callback of any stream.

Try sleeping longer before close()

When Oboe closes a stream, it will call requestStop() just in case that was not already called. Then it will sleep for a few milliseconds before actually closing the stream. This is designed to give time for all the callbacks to stop before closing and deleting the AAudio stream. You might want to experiment with increasing this sleep time.

setDelayBeforeCloseMillis() doc

Related Bugs

There were some bugs in Oboe and the Android framework that could cause crashes.

Bug #2035 was related to using Oboe to do sample rate conversion, or format or channelCount conversion. It broke the rules of using a shared_ptr with openStream. Fixed in Oboe V1.9.0.

A bug in AudioFlinger could cause an assert in releaseBuffer(). It could affect non-MMAP AAudio streams or OpenSL ES streams when disconnecting a headset. See TechNote_ReleaseBuffer. Fixed in Android R (11).

A bug in AudioTrack could cause data callbacks to run after the AAudio stream was closed and deleted. There is a workaround in Oboe that adds a short sleep between stopping and closing a stream. Fixed in Android R (11).

A bug in AudioTrack could cause a use after free inside AudioFlinger. See CrashUAF_ObtainReleaseBuffer. Fixed in Android U (14).

Clone this wiki locally