-
Notifications
You must be signed in to change notification settings - Fork 572
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
OboeTester: add a Rapid Cycle test (#2042)
This was an attempt to reproduce b/348615156 b/348615156 | P2 | [Oboe/OpenSLES] Deadlock is caused when close function is called immediately after stream playback.
- Loading branch information
Showing
11 changed files
with
438 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
/* | ||
* Copyright 2023 The Android Open Source Project | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#include <stdlib.h> | ||
#include <aaudio/AAudioExtensions.h> | ||
|
||
#include "common/OboeDebug.h" | ||
#include "common/AudioClock.h" | ||
#include "TestRapidCycle.h" | ||
|
||
using namespace oboe; | ||
|
||
// open start start an Oboe stream | ||
int32_t TestRapidCycle::start(bool useOpenSL) { | ||
mThreadEnabled = true; | ||
mCycleCount = 0; | ||
mCycleThread = std::thread([this, useOpenSL]() { | ||
cycleRapidly(useOpenSL); | ||
}); | ||
return 0; | ||
} | ||
int32_t TestRapidCycle::stop() { | ||
mThreadEnabled = false; | ||
mCycleThread.join(); | ||
return 0; | ||
} | ||
|
||
void TestRapidCycle::cycleRapidly(bool useOpenSL) { | ||
while(mThreadEnabled && (oneCycle(useOpenSL) == 0)); | ||
} | ||
|
||
int32_t TestRapidCycle::oneCycle(bool useOpenSL) { | ||
mCycleCount++; | ||
mDataCallback = std::make_shared<MyDataCallback>(); | ||
|
||
AudioStreamBuilder builder; | ||
oboe::Result result = builder.setFormat(oboe::AudioFormat::Float) | ||
->setAudioApi(useOpenSL ? oboe::AudioApi::OpenSLES : oboe::AudioApi::AAudio) | ||
->setPerformanceMode(oboe::PerformanceMode::LowLatency) | ||
->setChannelCount(kChannelCount) | ||
->setDataCallback(mDataCallback) | ||
->setUsage(oboe::Usage::Notification) | ||
->openStream(mStream); | ||
if (result != oboe::Result::OK) { | ||
return (int32_t) result; | ||
} | ||
|
||
mStream->setDelayBeforeCloseMillis(0); | ||
|
||
result = mStream->requestStart(); | ||
if (result != oboe::Result::OK) { | ||
mStream->close(); | ||
return (int32_t) result; | ||
} | ||
// Sleep for some random time. | ||
int32_t durationMicros = (int32_t)(drand48() * kMaxSleepMicros); | ||
LOGD("TestRapidCycle::oneCycle() - Sleep for %d micros", durationMicros); | ||
usleep(durationMicros); | ||
LOGD("TestRapidCycle::oneCycle() - Woke up, stop stream"); | ||
oboe::Result result1 = mStream->requestStop(); | ||
oboe::Result result2 = mStream->close(); | ||
return (int32_t)((result1 != oboe::Result::OK) ? result1 : result2); | ||
} | ||
|
||
// Callback that sleeps then touches the audio buffer. | ||
DataCallbackResult TestRapidCycle::MyDataCallback::onAudioReady( | ||
AudioStream *audioStream, | ||
void *audioData, | ||
int32_t numFrames) { | ||
float *floatData = (float *) audioData; | ||
const int numSamples = numFrames * kChannelCount; | ||
|
||
// Fill buffer with white noise. | ||
for (int i = 0; i < numSamples; i++) { | ||
floatData[i] = ((float)drand48() - 0.5f) * 2 * 0.1f; | ||
} | ||
|
||
return oboe::DataCallbackResult::Continue; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* | ||
* Copyright 2023 The Android Open Source Project | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#ifndef OBOETESTER_TEST_RAPID_CYCLE_H | ||
#define OBOETESTER_TEST_RAPID_CYCLE_H | ||
|
||
#include "oboe/Oboe.h" | ||
#include <thread> | ||
|
||
|
||
/** | ||
* Try to cause a crash by changing routing during a data callback. | ||
* We use Use::VoiceCommunication for the stream and | ||
* setSpeakerPhoneOn(b) to force a routing change. | ||
* This works best when connected to a BT headset. | ||
*/ | ||
class TestRapidCycle { | ||
public: | ||
|
||
int32_t start(bool useOpenSL); | ||
int32_t stop(); | ||
|
||
int32_t getCycleCount() { | ||
return mCycleCount.load(); | ||
} | ||
|
||
private: | ||
|
||
void cycleRapidly(bool useOpenSL); | ||
int32_t oneCycle(bool useOpenSL); | ||
|
||
class MyDataCallback : public oboe::AudioStreamDataCallback { public: | ||
|
||
MyDataCallback() {} | ||
|
||
oboe::DataCallbackResult onAudioReady( | ||
oboe::AudioStream *audioStream, | ||
void *audioData, | ||
int32_t numFrames) override; | ||
}; | ||
|
||
std::shared_ptr<oboe::AudioStream> mStream; | ||
std::shared_ptr<MyDataCallback> mDataCallback; | ||
std::atomic<int32_t> mCycleCount{0}; | ||
std::atomic<bool> mThreadEnabled{false}; | ||
std::thread mCycleThread; | ||
|
||
static constexpr int kChannelCount = 1; | ||
static constexpr int kMaxSleepMicros = 25000; | ||
}; | ||
|
||
#endif //OBOETESTER_TEST_RAPID_CYCLE_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
150 changes: 150 additions & 0 deletions
150
apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestRapidCycleActivity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
/* | ||
* Copyright 2023 The Android Open Source Project | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.mobileer.oboetester; | ||
|
||
import static com.mobileer.oboetester.TestAudioActivity.TAG; | ||
|
||
import android.content.Context; | ||
import android.media.AudioManager; | ||
import android.os.Bundle; | ||
import android.util.Log; | ||
import android.view.View; | ||
import android.view.WindowManager; | ||
import android.widget.Button; | ||
import android.widget.RadioButton; | ||
import android.widget.TextView; | ||
|
||
import androidx.appcompat.app.AppCompatActivity; | ||
|
||
/** | ||
* Try to hang streams by rapidly opening and closing. | ||
* See b/348615156 | ||
*/ | ||
public class TestRapidCycleActivity extends AppCompatActivity { | ||
|
||
private TextView mStatusView; | ||
private MyStreamSniffer mStreamSniffer; | ||
private AudioManager mAudioManager; | ||
private RadioButton mApiOpenSLButton; | ||
private RadioButton mApiAAudioButton; | ||
private Button mStartButton; | ||
private Button mStopButton; | ||
|
||
@Override | ||
protected void onCreate(Bundle savedInstanceState) { | ||
super.onCreate(savedInstanceState); | ||
setContentView(R.layout.activity_rapid_cycle); | ||
mStatusView = (TextView) findViewById(R.id.text_callback_status); | ||
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); | ||
|
||
mStartButton = (Button) findViewById(R.id.button_start_test); | ||
mStopButton = (Button) findViewById(R.id.button_stop_test); | ||
mApiOpenSLButton = (RadioButton) findViewById(R.id.audio_api_opensl); | ||
mApiOpenSLButton.setChecked(true); | ||
mApiAAudioButton = (RadioButton) findViewById(R.id.audio_api_aaudio); | ||
setButtonsEnabled(false); | ||
} | ||
|
||
public void onStartCycleTest(View view) { startCycleTest(); } | ||
public void onStopCycleTest(View view) { | ||
stopCycleTest(); | ||
} | ||
|
||
private void setButtonsEnabled(boolean running) { | ||
mStartButton.setEnabled(!running); | ||
mStopButton.setEnabled(running); | ||
mApiOpenSLButton.setEnabled(!running); | ||
mApiAAudioButton.setEnabled(!running); | ||
} | ||
|
||
// Change routing while the stream is playing. | ||
// Keep trying until we crash. | ||
protected class MyStreamSniffer extends Thread { | ||
boolean enabled = true; | ||
StringBuffer statusBuffer = new StringBuffer(); | ||
|
||
@Override | ||
public void run() { | ||
boolean useOpenSL = mApiOpenSLButton.isChecked(); | ||
startRapidCycleTest(useOpenSL); | ||
try { | ||
while (enabled) { | ||
statusBuffer = new StringBuffer(); | ||
sleep(100); | ||
log("#" + getCycleCount() + " open/close cycles\n"); | ||
} | ||
} catch (InterruptedException e) { | ||
} finally { | ||
stopRapidCycleTest(); | ||
} | ||
} | ||
|
||
// Log to screen and logcat. | ||
private void log(String text) { | ||
Log.d(TAG, "RapidCycle: " + text); | ||
statusBuffer.append(text); | ||
showStatus(statusBuffer.toString()); | ||
} | ||
|
||
// Stop the test thread. | ||
void finish() { | ||
enabled = false; | ||
interrupt(); | ||
try { | ||
join(2000); | ||
} catch (InterruptedException e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
} | ||
|
||
protected void showStatus(final String message) { | ||
runOnUiThread(new Runnable() { | ||
@Override | ||
public void run() { | ||
mStatusView.setText(message); | ||
} | ||
}); | ||
} | ||
|
||
private native int startRapidCycleTest(boolean useOpenSL); | ||
private native int stopRapidCycleTest(); | ||
private native int getCycleCount(); | ||
|
||
@Override | ||
public void onPause() { | ||
super.onPause(); | ||
Log.i(TAG, "onPause() called so stop the test ========================="); | ||
stopCycleTest(); | ||
} | ||
|
||
private void startCycleTest() { | ||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); | ||
setButtonsEnabled(true); | ||
mStreamSniffer = new MyStreamSniffer(); | ||
mStreamSniffer.start(); | ||
} | ||
|
||
private void stopCycleTest() { | ||
if (mStreamSniffer != null) { | ||
mStreamSniffer.finish(); | ||
mStreamSniffer = null; | ||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); | ||
setButtonsEnabled(false); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.