Merge "Embed an sdkinfo proto in the test media apex" into rvc-dev
diff --git a/MainlineFiles.cfg b/MainlineFiles.cfg
new file mode 100644
index 0000000..37d714c
--- /dev/null
+++ b/MainlineFiles.cfg
@@ -0,0 +1,34 @@
+#
+# mainline files for frameworks/av
+#
+# ignore comment (#) lines and blank lines
+# rest are path prefixes starting at root of the project
+# (so OWNERS, not frameworks/av/OWNERS)
+#
+# path
+# INCLUDE path
+# EXCLUDE path
+#
+# 'path' and 'INCLUDE path' are identical -- they both indicate that this path
+# is part of mainline
+# EXCLUDE indicates that this is not part of mainline,
+# so 'foo/' and 'EXCLUDE foo/nope'
+# means everything under foo/ is part of mainline EXCEPT foo/nope.
+# INCLUDE/EXCLUDE/INCLUDE nested structuring is not supported
+#
+# matching is purely prefix
+# so 'foo' will match 'foo', 'foo.c', 'foo/bar/baz'
+# if you want to exclude a directory, best to use a pattern like "foo/"
+#
+
+media/codec2/components/
+media/codecs/
+media/extractors/
+media/libstagefright/codecs/amrnb/
+media/libstagefright/codecs/amrwb/
+media/libstagefright/codecs/amrwbenc/
+media/libstagefright/codecs/common/
+media/libstagefright/codecs/flac/
+media/libstagefright/codecs/m4v_h263/
+media/libstagefright/codecs/mp3dec/
+media/libstagefright/mpeg2ts
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index 9fc0e17..29bccd5 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -278,16 +278,19 @@
C2ApiLevelSetting;
constexpr char C2_PARAMKEY_API_LEVEL[] = "api.level";
-enum C2Config::api_feature_t : uint64_t {
+C2ENUM(C2Config::api_feature_t, uint64_t,
API_REFLECTION = (1U << 0), ///< ability to list supported parameters
API_VALUES = (1U << 1), ///< ability to list supported values for each parameter
API_CURRENT_VALUES = (1U << 2), ///< ability to list currently supported values for each parameter
API_DEPENDENCY = (1U << 3), ///< have a defined parameter dependency
+ API_SAME_INPUT_BUFFER = (1U << 16), ///< supporting multiple input buffers
+ ///< backed by the same allocation
+
API_STREAMS = (1ULL << 32), ///< supporting variable number of streams
- API_TUNNELING = (1ULL << 48), ///< tunneling API
-};
+ API_TUNNELING = (1ULL << 48) ///< tunneling API
+)
// read-only
typedef C2GlobalParam<C2Setting, C2SimpleValueStruct<C2Config::api_feature_t>, kParamIndexApiFeatures>
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 8059ff6..907aa39 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -1686,6 +1686,9 @@
{
Mutexed<Output>::Locked output(mOutput);
+ if (!output->buffers) {
+ return false;
+ }
output->buffers->pushToStash(
buffer,
notifyClient,
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 79fa5ed..4520823 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -288,7 +288,7 @@
requestStop();
}
- logBufferState();
+ logReleaseBufferState();
setState(AAUDIO_STREAM_STATE_CLOSING);
aaudio_handle_t serviceStreamHandle = mServiceStreamHandle;
@@ -783,6 +783,14 @@
adjustedFrames = std::min(actualFrames, adjustedFrames);
}
+ if (adjustedFrames != mBufferSizeInFrames) {
+ android::mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETBUFFERSIZE)
+ .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, adjustedFrames)
+ .set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t) getXRunCount())
+ .record();
+ }
+
mBufferSizeInFrames = adjustedFrames;
ALOGV("%s(%d) returns %d", __func__, requestedFrames, adjustedFrames);
return (aaudio_result_t) adjustedFrames;
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 0644368..bc973bd 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -116,9 +116,10 @@
}
}
-void AudioStream::logBufferState() {
+void AudioStream::logReleaseBufferState() {
if (mMetricsId.size() > 0) {
android::mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_RELEASE)
.set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, (int32_t) getBufferSize())
.set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t) getXRunCount())
.record();
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 613a092..fb71c36 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -115,7 +115,7 @@
// log to MediaMetrics
virtual void logOpen();
- void logBufferState();
+ void logReleaseBufferState();
/**
* Free any hardware or system resources from the open() call.
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 853c0db..6e5110f 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -292,7 +292,7 @@
// Then call it from here
if (getState() != AAUDIO_STREAM_STATE_CLOSING) {
mAudioRecord->removeAudioDeviceCallback(mDeviceCallback);
- logBufferState();
+ logReleaseBufferState();
mAudioRecord.clear();
mFixedBlockWriter.close();
return AudioStream::release_l();
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 1120f05..ea08361 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -254,7 +254,7 @@
aaudio_result_t AudioStreamTrack::release_l() {
if (getState() != AAUDIO_STREAM_STATE_CLOSING) {
mAudioTrack->removeAudioDeviceCallback(mDeviceCallback);
- logBufferState();
+ logReleaseBufferState();
// TODO Investigate why clear() causes a hang in test_various.cpp
// if I call close() from a data callback.
// But the same thing in AudioRecord is OK!
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index a6e5f70..8935d57 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -233,6 +233,7 @@
srcs: ["test_steal_exclusive.cpp"],
shared_libs: [
"libaaudio",
+ "liblog",
"libbinder",
"libcutils",
"libutils",
diff --git a/media/libaaudio/tests/test_steal_exclusive.cpp b/media/libaaudio/tests/test_steal_exclusive.cpp
index 2a05910..e98934e 100644
--- a/media/libaaudio/tests/test_steal_exclusive.cpp
+++ b/media/libaaudio/tests/test_steal_exclusive.cpp
@@ -47,137 +47,268 @@
*/
#include <atomic>
+#include <mutex>
#include <stdio.h>
#include <thread>
#include <unistd.h>
+#include <android/log.h>
+
#include <aaudio/AAudio.h>
+#include <aaudio/AAudioTesting.h>
#define DEFAULT_TIMEOUT_NANOS ((int64_t)1000000000)
#define SOLO_DURATION_MSEC 2000
#define DUET_DURATION_MSEC 8000
#define SLEEP_DURATION_MSEC 500
+#define MODULE_NAME "stealAudio"
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, MODULE_NAME, __VA_ARGS__)
+
static const char * s_sharingModeToText(aaudio_sharing_mode_t mode) {
return (mode == AAUDIO_SHARING_MODE_EXCLUSIVE) ? "EXCLUSIVE"
: ((mode == AAUDIO_SHARING_MODE_SHARED) ? "SHARED"
: AAudio_convertResultToText(mode));
}
+static const char * s_performanceModeToText(aaudio_performance_mode_t mode) {
+ return (mode == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY) ? "LOWLAT"
+ : ((mode == AAUDIO_PERFORMANCE_MODE_NONE) ? "NONE"
+ : AAudio_convertResultToText(mode));
+}
+
+static aaudio_data_callback_result_t s_myDataCallbackProc(
+ AAudioStream * /* stream */,
+ void *userData,
+ void *audioData,
+ int32_t numFrames);
+
static void s_myErrorCallbackProc(
AAudioStream *stream,
void *userData,
aaudio_result_t error);
-struct AudioEngine {
- AAudioStream *stream = nullptr;
- std::thread *thread = nullptr;
- aaudio_direction_t direction = AAUDIO_DIRECTION_OUTPUT;
+class AudioEngine {
+public:
+
+ AudioEngine(const char *name) {
+ mName = name;
+ }
// These counters are read and written by the callback and the main thread.
- std::atomic<int32_t> framesRead{};
std::atomic<int32_t> framesCalled{};
std::atomic<int32_t> callbackCount{};
+ std::atomic<aaudio_sharing_mode_t> sharingMode{};
+ std::atomic<aaudio_performance_mode_t> performanceMode{};
+ std::atomic<bool> isMMap{false};
+ void setMaxRetries(int maxRetries) {
+ mMaxRetries = maxRetries;
+ }
+
+ void setOpenDelayMillis(int openDelayMillis) {
+ mOpenDelayMillis = openDelayMillis;
+ }
+
+ void restartStream() {
+ int retriesLeft = mMaxRetries;
+ aaudio_result_t result;
+ do {
+ closeAudioStream();
+ if (mOpenDelayMillis) usleep(mOpenDelayMillis * 1000);
+ openAudioStream(mDirection);
+ // It is possible for the stream to be disconnected, or stolen between the time
+ // it is opened and when it is started. If that happens then try again.
+ // If it was stolen then it should succeed the second time because there will already be
+ // a SHARED stream, which will not get stolen.
+ result = AAudioStream_requestStart(mStream);
+ printf("%s: AAudioStream_requestStart() returns %s\n",
+ mName.c_str(),
+ AAudio_convertResultToText(result));
+ } while (retriesLeft-- > 0 && result != AAUDIO_OK);
+ }
+
+ aaudio_data_callback_result_t onAudioReady(
+ void * /*audioData */,
+ int32_t numFrames) {
+ callbackCount++;
+ framesCalled += numFrames;
+ return AAUDIO_CALLBACK_RESULT_CONTINUE;
+ }
+
+ aaudio_result_t openAudioStream(aaudio_direction_t direction) {
+ std::lock_guard<std::mutex> lock(mLock);
+
+ AAudioStreamBuilder *builder = nullptr;
+ mDirection = direction;
+
+ // Use an AAudioStreamBuilder to contain requested parameters.
+ aaudio_result_t result = AAudio_createStreamBuilder(&builder);
+ if (result != AAUDIO_OK) {
+ printf("AAudio_createStreamBuilder returned %s",
+ AAudio_convertResultToText(result));
+ return result;
+ }
+
+ // Request stream properties.
+ AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
+ AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+ AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);
+ AAudioStreamBuilder_setDirection(builder, direction);
+ AAudioStreamBuilder_setDataCallback(builder, s_myDataCallbackProc, this);
+ AAudioStreamBuilder_setErrorCallback(builder, s_myErrorCallbackProc, this);
+
+ // Create an AAudioStream using the Builder.
+ result = AAudioStreamBuilder_openStream(builder, &mStream);
+ AAudioStreamBuilder_delete(builder);
+ builder = nullptr;
+ if (result != AAUDIO_OK) {
+ printf("AAudioStreamBuilder_openStream returned %s",
+ AAudio_convertResultToText(result));
+ }
+
+ // See what kind of stream we actually opened.
+ int32_t deviceId = AAudioStream_getDeviceId(mStream);
+ sharingMode = AAudioStream_getSharingMode(mStream);
+ performanceMode = AAudioStream_getPerformanceMode(mStream);
+ isMMap = AAudioStream_isMMapUsed(mStream);
+ printf("%s: opened: deviceId = %3d, sharingMode = %s, perf = %s, %s --------\n",
+ mName.c_str(),
+ deviceId,
+ s_sharingModeToText(sharingMode),
+ s_performanceModeToText(performanceMode),
+ (isMMap ? "MMAP" : "Legacy")
+ );
+
+ return result;
+ }
+
+ aaudio_result_t closeAudioStream() {
+ std::lock_guard<std::mutex> lock(mLock);
+ aaudio_result_t result = AAUDIO_OK;
+ if (mStream != nullptr) {
+ result = AAudioStream_close(mStream);
+ if (result != AAUDIO_OK) {
+ printf("AAudioStream_close returned %s\n",
+ AAudio_convertResultToText(result));
+ }
+ mStream = nullptr;
+ }
+ return result;
+ }
+
+ /**
+ * @return 0 is OK, -1 for error
+ */
+ int checkEnginePositions() {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mStream == nullptr) return 0;
+
+ const int64_t framesRead = AAudioStream_getFramesRead(mStream);
+ const int64_t framesWritten = AAudioStream_getFramesWritten(mStream);
+ const int32_t delta = (int32_t)(framesWritten - framesRead);
+ printf("%s: playing framesRead = %7d, framesWritten = %7d"
+ ", delta = %4d, framesCalled = %6d, callbackCount = %4d\n",
+ mName.c_str(),
+ (int32_t) framesRead,
+ (int32_t) framesWritten,
+ delta,
+ framesCalled.load(),
+ callbackCount.load()
+ );
+ if (delta > AAudioStream_getBufferCapacityInFrames(mStream)) {
+ printf("ERROR - delta > capacity\n");
+ return -1;
+ }
+ return 0;
+ }
+
+ aaudio_result_t start() {
+ std::lock_guard<std::mutex> lock(mLock);
+ reset();
+ if (mStream == nullptr) return 0;
+ return AAudioStream_requestStart(mStream);
+ }
+
+ aaudio_result_t stop() {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mStream == nullptr) return 0;
+ return AAudioStream_requestStop(mStream);
+ }
+
+ bool hasAdvanced() {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mStream == nullptr) return 0;
+ if (mDirection == AAUDIO_DIRECTION_OUTPUT) {
+ return AAudioStream_getFramesRead(mStream) > 0;
+ } else {
+ return AAudioStream_getFramesWritten(mStream) > 0;
+ }
+ }
+
+ aaudio_result_t verify() {
+ int errorCount = 0;
+ if (hasAdvanced()) {
+ printf("%s: stream is running => PASS\n", mName.c_str());
+ } else {
+ errorCount++;
+ printf("%s: stream should be running => FAIL!!\n", mName.c_str());
+ }
+
+ if (isMMap) {
+ printf("%s: data path is MMAP => PASS\n", mName.c_str());
+ } else {
+ errorCount++;
+ printf("%s: data path is Legacy! => FAIL\n", mName.c_str());
+ }
+
+ // Check for PASS/FAIL
+ if (sharingMode == AAUDIO_SHARING_MODE_SHARED) {
+ printf("%s: mode is SHARED => PASS\n", mName.c_str());
+ } else {
+ errorCount++;
+ printf("%s: modes is EXCLUSIVE => FAIL!!\n", mName.c_str());
+ }
+ return errorCount ? AAUDIO_ERROR_INVALID_FORMAT : AAUDIO_OK;
+ }
+
+private:
void reset() {
- framesRead.store(0);
framesCalled.store(0);
callbackCount.store(0);
}
+
+ AAudioStream *mStream = nullptr;
+ aaudio_direction_t mDirection = AAUDIO_DIRECTION_OUTPUT;
+ std::mutex mLock;
+ std::string mName;
+ int mMaxRetries = 1;
+ int mOpenDelayMillis = 0;
};
// Callback function that fills the audio output buffer.
static aaudio_data_callback_result_t s_myDataCallbackProc(
- AAudioStream *stream,
+ AAudioStream * /* stream */,
void *userData,
void *audioData,
int32_t numFrames
) {
- (void) audioData;
- (void) numFrames;
- AudioEngine *engine = (struct AudioEngine *)userData;
- engine->callbackCount++;
-
- engine->framesRead = (int32_t)AAudioStream_getFramesRead(stream);
- engine->framesCalled += numFrames;
- return AAUDIO_CALLBACK_RESULT_CONTINUE;
-}
-
-static aaudio_result_t s_OpenAudioStream(struct AudioEngine *engine,
- aaudio_direction_t direction) {
- AAudioStreamBuilder *builder = nullptr;
- engine->direction = direction;
-
- // Use an AAudioStreamBuilder to contain requested parameters.
- aaudio_result_t result = AAudio_createStreamBuilder(&builder);
- if (result != AAUDIO_OK) {
- printf("AAudio_createStreamBuilder returned %s",
- AAudio_convertResultToText(result));
- return result;
- }
-
- // Request stream properties.
- AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
- AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
- AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);
- AAudioStreamBuilder_setDirection(builder, direction);
- AAudioStreamBuilder_setDataCallback(builder, s_myDataCallbackProc, engine);
- AAudioStreamBuilder_setErrorCallback(builder, s_myErrorCallbackProc, engine);
-
- // Create an AAudioStream using the Builder.
- result = AAudioStreamBuilder_openStream(builder, &engine->stream);
- AAudioStreamBuilder_delete(builder);
- builder = nullptr;
- if (result != AAUDIO_OK) {
- printf("AAudioStreamBuilder_openStream returned %s",
- AAudio_convertResultToText(result));
- }
-
- // See see what kind of stream we actually opened.
- int32_t deviceId = AAudioStream_getDeviceId(engine->stream);
- aaudio_sharing_mode_t actualSharingMode = AAudioStream_getSharingMode(engine->stream);
- printf("-------- opened: deviceId = %3d, actualSharingMode = %s\n",
- deviceId,
- s_sharingModeToText(actualSharingMode));
-
- return result;
-}
-
-static aaudio_result_t s_CloseAudioStream(struct AudioEngine *engine) {
- aaudio_result_t result = AAUDIO_OK;
- if (engine->stream != nullptr) {
- result = AAudioStream_close(engine->stream);
- if (result != AAUDIO_OK) {
- printf("AAudioStream_close returned %s\n",
- AAudio_convertResultToText(result));
- }
- engine->stream = nullptr;
- }
- return result;
+ AudioEngine *engine = (AudioEngine *)userData;
+ return engine->onAudioReady(audioData, numFrames);
}
static void s_myRestartStreamProc(void *userData) {
+ LOGI("%s() called", __func__);
printf("%s() - restart in separate thread\n", __func__);
AudioEngine *engine = (AudioEngine *) userData;
- int retriesLeft = 1;
- aaudio_result_t result;
- do {
- s_CloseAudioStream(engine);
- s_OpenAudioStream(engine, engine->direction);
- // It is possible for the stream to be disconnected, or stolen between the time
- // it is opened and when it is started. If that happens then try again.
- // If it was stolen then it should succeed the second time because there will already be
- // a SHARED stream, which will not get stolen.
- result = AAudioStream_requestStart(engine->stream);
- printf("%s() - AAudioStream_requestStart() returns %s\n", __func__,
- AAudio_convertResultToText(result));
- } while (retriesLeft-- > 0 && result != AAUDIO_OK);
+ engine->restartStream();
}
static void s_myErrorCallbackProc(
AAudioStream * /* stream */,
void *userData,
aaudio_result_t error) {
+ LOGI("%s() called", __func__);
printf("%s() - error = %s\n", __func__, AAudio_convertResultToText(error));
// Handle error on a separate thread.
std::thread t(s_myRestartStreamProc, userData);
@@ -185,42 +316,20 @@
}
static void s_usage() {
- printf("test_steal_exclusive [-i]\n");
+ printf("test_steal_exclusive [-i] [-r{maxRetries}] [-d{delay}]\n");
printf(" -i direction INPUT, otherwise OUTPUT\n");
+ printf(" -d delay open by milliseconds, default = 0\n");
+ printf(" -r max retries in the error callback, default = 1\n");
}
-/**
- * @return 0 is OK, -1 for error
- */
-static int s_checkEnginePositions(AudioEngine *engine) {
- if (engine->stream == nullptr) return 0; // race condition with onError procs!
-
- const int64_t framesRead = AAudioStream_getFramesRead(engine->stream);
- const int64_t framesWritten = AAudioStream_getFramesWritten(engine->stream);
- const int32_t delta = (int32_t)(framesWritten - framesRead);
- printf("playing framesRead = %7d, framesWritten = %7d"
- ", delta = %4d, framesCalled = %6d, callbackCount = %4d\n",
- (int32_t) framesRead,
- (int32_t) framesWritten,
- delta,
- engine->framesCalled.load(),
- engine->callbackCount.load()
- );
- if (delta > AAudioStream_getBufferCapacityInFrames(engine->stream)) {
- printf("ERROR - delta > capacity\n");
- return -1;
- }
- return 0;
-}
-
-int main(int argc, char **argv) {
- (void) argc;
- (void) argv;
- struct AudioEngine victim;
- struct AudioEngine thief;
+int main(int argc, char ** argv) {
+ AudioEngine victim("victim");
+ AudioEngine thief("thief");
aaudio_direction_t direction = AAUDIO_DIRECTION_OUTPUT;
aaudio_result_t result = AAUDIO_OK;
int errorCount = 0;
+ int maxRetries = 1;
+ int openDelayMillis = 0;
// Make printf print immediately so that debug info is not stuck
// in a buffer if we hang or crash.
@@ -234,9 +343,15 @@
if (arg[0] == '-') {
char option = arg[1];
switch (option) {
+ case 'd':
+ openDelayMillis = atoi(&arg[2]);
+ break;
case 'i':
direction = AAUDIO_DIRECTION_INPUT;
break;
+ case 'r':
+ maxRetries = atoi(&arg[2]);
+ break;
default:
s_usage();
exit(EXIT_FAILURE);
@@ -249,16 +364,34 @@
}
}
- result = s_OpenAudioStream(&victim, direction);
+ victim.setOpenDelayMillis(openDelayMillis);
+ thief.setOpenDelayMillis(openDelayMillis);
+ victim.setMaxRetries(maxRetries);
+ thief.setMaxRetries(maxRetries);
+
+ result = victim.openAudioStream(direction);
if (result != AAUDIO_OK) {
printf("s_OpenAudioStream victim returned %s\n",
AAudio_convertResultToText(result));
errorCount++;
}
- victim.reset();
+
+ if (victim.sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+ printf("Victim modes is EXCLUSIVE => OK\n");
+ } else {
+ printf("Victim modes should be EXCLUSIVE => test not valid!\n");
+ goto onerror;
+ }
+
+ if (victim.isMMap) {
+ printf("Victim data path is MMAP => OK\n");
+ } else {
+ printf("Victim data path is Legacy! => test not valid\n");
+ goto onerror;
+ }
// Start stream.
- result = AAudioStream_requestStart(victim.stream);
+ result = victim.start();
printf("AAudioStream_requestStart(VICTIM) returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
if (result != AAUDIO_OK) {
errorCount++;
@@ -267,77 +400,69 @@
if (result == AAUDIO_OK) {
const int watchLoops = SOLO_DURATION_MSEC / SLEEP_DURATION_MSEC;
for (int i = watchLoops; i > 0; i--) {
- errorCount += s_checkEnginePositions(&victim) ? 1 : 0;
+ errorCount += victim.checkEnginePositions() ? 1 : 0;
usleep(SLEEP_DURATION_MSEC * 1000);
}
}
- printf("Try to start the THIEF stream that may steal the VICTIM MMAP resource -----\n");
- result = s_OpenAudioStream(&thief, direction);
+ printf("Trying to start the THIEF stream, which may steal the VICTIM MMAP resource -----\n");
+ result = thief.openAudioStream(direction);
if (result != AAUDIO_OK) {
printf("s_OpenAudioStream victim returned %s\n",
AAudio_convertResultToText(result));
errorCount++;
}
- thief.reset();
// Start stream.
- result = AAudioStream_requestStart(thief.stream);
+ result = thief.start();
printf("AAudioStream_requestStart(THIEF) returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
if (result != AAUDIO_OK) {
errorCount++;
}
- printf("You might enjoy plugging in a headset now to see what happens...\n");
+
+ // Give stream time to advance.
+ usleep(SLEEP_DURATION_MSEC * 1000);
+
+ if (victim.verify()) {
+ errorCount++;
+ goto onerror;
+ }
+ if (thief.verify()) {
+ errorCount++;
+ goto onerror;
+ }
+
+ LOGI("Both streams running. Ask user to plug in headset. ====");
+ printf("\n====\nPlease PLUG IN A HEADSET now!\n====\n\n");
if (result == AAUDIO_OK) {
const int watchLoops = DUET_DURATION_MSEC / SLEEP_DURATION_MSEC;
for (int i = watchLoops; i > 0; i--) {
- printf("victim: ");
- errorCount += s_checkEnginePositions(&victim) ? 1 : 0;
- printf(" thief: ");
- errorCount += s_checkEnginePositions(&thief) ? 1 : 0;
+ errorCount += victim.checkEnginePositions() ? 1 : 0;
+ errorCount += thief.checkEnginePositions() ? 1 : 0;
usleep(SLEEP_DURATION_MSEC * 1000);
}
}
- // Check for PASS/FAIL
- aaudio_sharing_mode_t victimSharingMode = AAudioStream_getSharingMode(victim.stream);
- aaudio_sharing_mode_t thiefSharingMode = AAudioStream_getSharingMode(thief.stream);
- printf("victimSharingMode = %s, thiefSharingMode = %s, - ",
- s_sharingModeToText(victimSharingMode),
- s_sharingModeToText(thiefSharingMode));
- if ((victimSharingMode == AAUDIO_SHARING_MODE_SHARED)
- && (thiefSharingMode == AAUDIO_SHARING_MODE_SHARED)) {
- printf("Both modes are SHARED => PASS\n");
- } else {
- errorCount++;
- printf("Both modes should be SHARED => FAIL!!\n");
- }
+ errorCount += victim.verify() ? 1 : 0;
+ errorCount += thief.verify() ? 1 : 0;
- const int64_t victimFramesRead = AAudioStream_getFramesRead(victim.stream);
- const int64_t thiefFramesRead = AAudioStream_getFramesRead(thief.stream);
- printf("victimFramesRead = %d, thiefFramesRead = %d, - ",
- (int)victimFramesRead, (int)thiefFramesRead);
- if (victimFramesRead > 0 && thiefFramesRead > 0) {
- printf("Both streams are running => PASS\n");
- } else {
- errorCount++;
- printf("Both streams should be running => FAIL!!\n");
- }
-
- result = AAudioStream_requestStop(victim.stream);
+ result = victim.stop();
printf("AAudioStream_requestStop() returned %d <<<<<<<<<<<<<<<<<<<<<\n", result);
if (result != AAUDIO_OK) {
+ printf("stop result = %d = %s\n", result, AAudio_convertResultToText(result));
errorCount++;
}
- result = AAudioStream_requestStop(thief.stream);
+ result = thief.stop();
printf("AAudioStream_requestStop() returned %d <<<<<<<<<<<<<<<<<<<<<\n", result);
if (result != AAUDIO_OK) {
+ printf("stop result = %d = %s\n", result, AAudio_convertResultToText(result));
errorCount++;
}
- s_CloseAudioStream(&victim);
- s_CloseAudioStream(&thief);
+onerror:
+ victim.closeAudioStream();
+ thief.closeAudioStream();
printf("aaudio result = %d = %s\n", result, AAudio_convertResultToText(result));
printf("test %s\n", errorCount ? "FAILED" : "PASSED");
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index dd84511..005d358 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -639,9 +639,17 @@
status_t AudioTrack::start()
{
- const int64_t beginNs = systemTime();
AutoMutex lock(mLock);
+ if (mState == STATE_ACTIVE) {
+ return INVALID_OPERATION;
+ }
+
+ ALOGV("%s(%d): prior state:%s", __func__, mPortId, stateToString(mState));
+
+ // Defer logging here due to OpenSL ES repeated start calls.
+ // TODO(b/154868033) after fix, restore this logging back to the beginning of start().
+ const int64_t beginNs = systemTime();
status_t status = NO_ERROR; // logged: make sure to set this before returning.
mediametrics::Defer defer([&] {
mediametrics::LogItem(mMetricsId)
@@ -655,12 +663,6 @@
.set(AMEDIAMETRICS_PROP_STATUS, (int32_t)status)
.record(); });
- ALOGV("%s(%d): prior state:%s", __func__, mPortId, stateToString(mState));
-
- if (mState == STATE_ACTIVE) {
- status = INVALID_OPERATION;
- return status;
- }
mInUnderrun = true;
@@ -785,8 +787,9 @@
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
.set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
+ .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, (int32_t)mProxy->getBufferSizeInFrames())
+ .set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t) getUnderrunCount_l())
.record();
- logBufferSizeUnderruns();
});
ALOGV("%s(%d): prior state:%s", __func__, mPortId, stateToString(mState));
@@ -1139,16 +1142,6 @@
return NO_ERROR;
}
-void AudioTrack::logBufferSizeUnderruns() {
- LOG_ALWAYS_FATAL_IF(mMetricsId.size() == 0, "mMetricsId is empty!");
- ALOGD("%s(), mMetricsId = %s", __func__, mMetricsId.c_str());
- // FIXME THis hangs! Why?
-// android::mediametrics::LogItem(mMetricsId)
-// .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, (int32_t) getBufferSizeInFrames())
-// .set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t) getUnderrunCount())
-// .record();
-}
-
ssize_t AudioTrack::setBufferSizeInFrames(size_t bufferSizeInFrames)
{
AutoMutex lock(mLock);
@@ -1163,7 +1156,11 @@
ssize_t originalBufferSize = mProxy->getBufferSizeInFrames();
ssize_t finalBufferSize = mProxy->setBufferSizeInFrames((uint32_t) bufferSizeInFrames);
if (originalBufferSize != finalBufferSize) {
- logBufferSizeUnderruns();
+ android::mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETBUFFERSIZE)
+ .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, (int32_t)mProxy->getBufferSizeInFrames())
+ .set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t)getUnderrunCount_l())
+ .record();
}
return finalBufferSize;
}
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 17af7d4..0dbd842 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -1274,8 +1274,6 @@
std::string mMetricsId; // GUARDED_BY(mLock), could change in createTrack_l().
std::string mCallerName; // for example "aaudio"
- void logBufferSizeUnderruns();
-
private:
class AudioTrackCallback : public media::BnAudioTrackCallback {
public:
diff --git a/media/libaudioprocessing/AudioMixerBase.cpp b/media/libaudioprocessing/AudioMixerBase.cpp
index 64f91fe..a54e22f 100644
--- a/media/libaudioprocessing/AudioMixerBase.cpp
+++ b/media/libaudioprocessing/AudioMixerBase.cpp
@@ -1500,7 +1500,7 @@
ALOGVV("track__Resample\n");
mResampler->setSampleRate(sampleRate);
const bool ramp = needsRamp();
- if (MIXTYPE == MIXTYPE_MONOEXPAND || MIXTYPE == MIXTYPE_STEREOEXPAND
+ if (MIXTYPE == MIXTYPE_MONOEXPAND || MIXTYPE == MIXTYPE_STEREOEXPAND // custom volume handling
|| ramp || aux != NULL) {
// if ramp: resample with unity gain to temp buffer and scale/mix in 2nd step.
// if aux != NULL: resample with unity gain to temp buffer then apply send level.
diff --git a/media/libaudioprocessing/AudioMixerOps.h b/media/libaudioprocessing/AudioMixerOps.h
index 2748182..80bd093 100644
--- a/media/libaudioprocessing/AudioMixerOps.h
+++ b/media/libaudioprocessing/AudioMixerOps.h
@@ -236,7 +236,8 @@
|| MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
|| MIXTYPE == MIXTYPE_STEREOEXPAND);
auto proc = [](auto& a, const auto& b) {
- if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL) {
+ if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
+ || MIXTYPE == MIXTYPE_STEREOEXPAND) {
a += b;
} else {
a = b;
diff --git a/media/libmediametrics/include/MediaMetricsConstants.h b/media/libmediametrics/include/MediaMetricsConstants.h
index b916a78..0906411 100644
--- a/media/libmediametrics/include/MediaMetricsConstants.h
+++ b/media/libmediametrics/include/MediaMetricsConstants.h
@@ -175,10 +175,12 @@
#define AMEDIAMETRICS_PROP_EVENT_VALUE_OPEN "open"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_PAUSE "pause" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_READPARAMETERS "readParameters" // Thread
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_RELEASE "release"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_RESTORE "restore"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETMODE "setMode" // AudioFlinger
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETBUFFERSIZE "setBufferSize" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETPLAYBACKPARAM "setPlaybackParam" // AudioTrack
-#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME "setVoiceVolume" // AudioFlinger
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME "setVoiceVolume" // AudioFlinger
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOLUME "setVolume" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_START "start" // AudioTrack, AudioRecord
#define AMEDIAMETRICS_PROP_EVENT_VALUE_STOP "stop" // AudioTrack, AudioRecord
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index b902cbc..4c4d228 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -2401,8 +2401,8 @@
int32_t err;
CHECK(msg->findInt32("errno", &err));
// Stop tracks' threads and main writer thread.
- notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, err);
stop();
+ notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, err);
break;
}
// fallocate() failed, hence notify app about it and stop().
@@ -2410,9 +2410,10 @@
ALOGE("kWhatFallocateError");
int32_t err;
CHECK(msg->findInt32("errno", &err));
+ // Stop tracks' threads and main writer thread.
+ stop();
//TODO: introduce a suitable MEDIA_RECORDER_ERROR_* instead MEDIA_RECORDER_ERROR_UNKNOWN?
notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, err);
- stop();
break;
}
default:
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index b90e6bd..7f5e762 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -57,6 +57,7 @@
#include <media/stagefright/BufferProducerWrapper.h>
#include <media/stagefright/CCodec.h>
#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
@@ -95,9 +96,12 @@
static const char *kCodecCrypto = "android.media.mediacodec.crypto"; /* 0,1 */
static const char *kCodecProfile = "android.media.mediacodec.profile"; /* 0..n */
static const char *kCodecLevel = "android.media.mediacodec.level"; /* 0..n */
+static const char *kCodecBitrateMode = "android.media.mediacodec.bitrate_mode"; /* CQ/VBR/CBR */
+static const char *kCodecBitrate = "android.media.mediacodec.bitrate"; /* 0..n */
static const char *kCodecMaxWidth = "android.media.mediacodec.maxwidth"; /* 0..n */
static const char *kCodecMaxHeight = "android.media.mediacodec.maxheight"; /* 0..n */
static const char *kCodecError = "android.media.mediacodec.errcode";
+static const char *kCodecLifetimeMs = "android.media.mediacodec.lifetimeMs"; /* 0..n ms*/
static const char *kCodecErrorState = "android.media.mediacodec.errstate";
static const char *kCodecLatencyMax = "android.media.mediacodec.latency.max"; /* in us */
static const char *kCodecLatencyMin = "android.media.mediacodec.latency.min"; /* in us */
@@ -619,7 +623,6 @@
mFlags(0),
mStickyError(OK),
mSoftRenderer(NULL),
- mMetricsHandle(0),
mIsVideo(false),
mVideoWidth(0),
mVideoHeight(0),
@@ -679,6 +682,8 @@
mIndexOfFirstFrameWhenLowLatencyOn = -1;
mInputBufferCounter = 0;
}
+
+ mLifetimeStartNs = systemTime(SYSTEM_TIME_MONOTONIC);
}
void MediaCodec::updateMediametrics() {
@@ -687,7 +692,6 @@
return;
}
-
if (mLatencyHist.getCount() != 0 ) {
mediametrics_setInt64(mMetricsHandle, kCodecLatencyMax, mLatencyHist.getMax());
mediametrics_setInt64(mMetricsHandle, kCodecLatencyMin, mLatencyHist.getMin());
@@ -703,6 +707,11 @@
if (mLatencyUnknown > 0) {
mediametrics_setInt64(mMetricsHandle, kCodecLatencyUnknown, mLatencyUnknown);
}
+ if (mLifetimeStartNs > 0) {
+ nsecs_t lifetime = systemTime(SYSTEM_TIME_MONOTONIC) - mLifetimeStartNs;
+ lifetime = lifetime / (1000 * 1000); // emitted in ms, truncated not rounded
+ mediametrics_setInt64(mMetricsHandle, kCodecLifetimeMs, lifetime);
+ }
{
Mutex::Autolock al(mLatencyLock);
@@ -740,7 +749,6 @@
}
}
-
// spit the data (if any) into the supplied analytics record
if (recentHist.getCount()!= 0 ) {
mediametrics_setInt64(item, kCodecRecentLatencyMax, recentHist.getMax());
@@ -2309,6 +2317,8 @@
// meaningful and confusing for an encoder in a transcoder scenario
mInputFormat->setInt32("allow-frame-drop", mAllowFrameDroppingBySurface);
}
+ sp<AMessage> interestingFormat =
+ (mFlags & kFlagIsEncoder) ? mOutputFormat : mInputFormat;
ALOGV("[%s] configured as input format: %s, output format: %s",
mComponentName.c_str(),
mInputFormat->debugString(4).c_str(),
@@ -2322,6 +2332,7 @@
(new AMessage)->postReply(mReplyID);
// augment our media metrics info, now that we know more things
+ // such as what the codec extracted from any CSD passed in.
if (mMetricsHandle != 0) {
sp<AMessage> format;
if (mConfigureMsg != NULL &&
@@ -2333,6 +2344,30 @@
mime.c_str());
}
}
+ // perhaps video only?
+ int32_t profile = 0;
+ if (interestingFormat->findInt32("profile", &profile)) {
+ mediametrics_setInt32(mMetricsHandle, kCodecProfile, profile);
+ }
+ int32_t level = 0;
+ if (interestingFormat->findInt32("level", &level)) {
+ mediametrics_setInt32(mMetricsHandle, kCodecLevel, level);
+ }
+ // bitrate and bitrate mode, encoder only
+ if (mFlags & kFlagIsEncoder) {
+ // encoder specific values
+ int32_t bitrate_mode = -1;
+ if (mOutputFormat->findInt32(KEY_BITRATE_MODE, &bitrate_mode)) {
+ mediametrics_setCString(mMetricsHandle, kCodecBitrateMode,
+ asString_BitrateMode(bitrate_mode));
+ }
+ int32_t bitrate = -1;
+ if (mOutputFormat->findInt32(KEY_BIT_RATE, &bitrate)) {
+ mediametrics_setInt32(mMetricsHandle, kCodecBitrate, bitrate);
+ }
+ } else {
+ // decoder specific values
+ }
}
break;
}
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index 809f298..cab4ebd 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -48,8 +48,7 @@
MediaMuxer::MediaMuxer(int fd, OutputFormat format)
: mFormat(format),
- mState(UNINITIALIZED),
- mError(OK) {
+ mState(UNINITIALIZED) {
if (isMp4Format(format)) {
mWriter = new MPEG4Writer(fd);
} else if (format == OUTPUT_FORMAT_WEBM) {
@@ -59,7 +58,6 @@
}
if (mWriter != NULL) {
- mWriter->setMuxerListener(this);
mFileMeta = new MetaData;
if (format == OUTPUT_FORMAT_HEIF) {
// Note that the key uses recorder file types.
@@ -157,26 +155,16 @@
status_t MediaMuxer::stop() {
Mutex::Autolock autoLock(mMuxerLock);
- if (mState == STARTED || mState == ERROR) {
+ if (mState == STARTED) {
mState = STOPPED;
for (size_t i = 0; i < mTrackList.size(); i++) {
if (mTrackList[i]->stop() != OK) {
return INVALID_OPERATION;
}
}
- // Unlock this mutex to allow notify to be called during stop process.
- mMuxerLock.unlock();
status_t err = mWriter->stop();
- mMuxerLock.lock();
- if (err != OK || mError != OK) {
- ALOGE("stop err: %d, mError:%d", err, mError);
- }
- /* Prioritize mError over err as writer would have got stopped on any
- * internal error and notified muxer already. Clients might issue
- * stop again later, and mWriter->stop() would return success.
- */
- if (mError != OK) {
- err = mError;
+ if (err != OK) {
+ ALOGE("stop() err: %d", err);
}
return err;
} else {
@@ -232,29 +220,4 @@
return currentTrack->pushBuffer(mediaBuffer);
}
-void MediaMuxer::notify(int msg, int ext1, int ext2) {
- switch (msg) {
- case MEDIA_RECORDER_EVENT_ERROR:
- case MEDIA_RECORDER_TRACK_EVENT_ERROR: {
- Mutex::Autolock autoLock(mMuxerLock);
- mState = ERROR;
- mError = ext2;
- ALOGW("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
- break;
- }
- case MEDIA_RECORDER_EVENT_INFO: {
- if (ext1 == MEDIA_RECORDER_INFO_UNKNOWN) {
- Mutex::Autolock autoLock(mMuxerLock);
- mState = ERROR;
- mError = ext2;
- ALOGW("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
- }
- break;
- }
- default:
- // Ignore INFO and other notifications for now.
- break;
- }
-}
-
} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 63a9dad..7f308c0 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -371,7 +371,8 @@
sp<Surface> mSurface;
SoftwareRenderer *mSoftRenderer;
- mediametrics_handle_t mMetricsHandle;
+ mediametrics_handle_t mMetricsHandle = 0;
+ nsecs_t mLifetimeStartNs = 0;
void initMediametrics();
void updateMediametrics();
void flushMediametrics();
diff --git a/media/libstagefright/include/media/stagefright/MediaMuxer.h b/media/libstagefright/include/media/stagefright/MediaMuxer.h
index 7c75f74..a1b9465 100644
--- a/media/libstagefright/include/media/stagefright/MediaMuxer.h
+++ b/media/libstagefright/include/media/stagefright/MediaMuxer.h
@@ -117,8 +117,6 @@
status_t writeSampleData(const sp<ABuffer> &buffer, size_t trackIndex,
int64_t timeUs, uint32_t flags) ;
- void notify(int msg, int ext1, int ext2);
-
private:
const OutputFormat mFormat;
sp<MediaWriter> mWriter;
@@ -130,11 +128,9 @@
UNINITIALIZED,
INITIALIZED,
STARTED,
- STOPPED,
- ERROR
+ STOPPED
};
State mState;
- status_t mError;
DISALLOW_EVIL_CONSTRUCTORS(MediaMuxer);
};
diff --git a/media/libstagefright/include/media/stagefright/MediaWriter.h b/media/libstagefright/include/media/stagefright/MediaWriter.h
index 08e54b3..1f4fbcb 100644
--- a/media/libstagefright/include/media/stagefright/MediaWriter.h
+++ b/media/libstagefright/include/media/stagefright/MediaWriter.h
@@ -21,7 +21,7 @@
#include <utils/RefBase.h>
#include <media/stagefright/MediaSource.h>
#include <media/IMediaRecorderClient.h>
-#include <media/stagefright/MediaMuxer.h>
+#include <media/mediarecorder.h>
namespace android {
@@ -46,7 +46,6 @@
virtual void setListener(const sp<IMediaRecorderClient>& listener) {
mListener = listener;
}
- virtual void setMuxerListener(const wp<MediaMuxer>& muxer) { mMuxer = muxer; }
virtual status_t dump(int /*fd*/, const Vector<String16>& /*args*/) {
return OK;
@@ -61,17 +60,20 @@
int64_t mMaxFileSizeLimitBytes;
int64_t mMaxFileDurationLimitUs;
sp<IMediaRecorderClient> mListener;
- wp<MediaMuxer> mMuxer;
void notify(int msg, int ext1, int ext2) {
- ALOG(LOG_VERBOSE, "MediaWriter", "notify msg:%d, ext1:%d, ext2:%d", msg, ext1, ext2);
+ if (msg == MEDIA_RECORDER_TRACK_EVENT_INFO || msg == MEDIA_RECORDER_TRACK_EVENT_ERROR) {
+ uint32_t trackId = (ext1 >> 28) & 0xf;
+ int type = ext1 & 0xfffffff;
+ ALOG(LOG_VERBOSE, "MediaWriter", "Track event err/info msg:%d, trackId:%u, type:%d,"
+ "val:%d", msg, trackId, type, ext2);
+ } else {
+ ALOG(LOG_VERBOSE, "MediaWriter", "Recorder event msg:%d, ext1:%d, ext2:%d",
+ msg, ext1, ext2);
+ }
if (mListener != nullptr) {
mListener->notify(msg, ext1, ext2);
}
- sp<MediaMuxer> muxer = mMuxer.promote();
- if (muxer != nullptr) {
- muxer->notify(msg, ext1, ext2);
- }
}
private:
MediaWriter(const MediaWriter &);
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 5b2f6de..ac42373 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -214,6 +214,80 @@
}
}
+template <typename T>
+inline static T *asSetting(void *setting /* nonnull */, size_t size) {
+ // no need to check internally stored size as that is outside of sanitizing
+ // the underlying buffer's size is the one passed into this method.
+ if (size < sizeof(T)) {
+ return nullptr;
+ }
+
+ return (T *)setting;
+}
+
+inline static void sanitize(OMX_CONFIG_CONTAINERNODEIDTYPE *s) {
+ s->cNodeName = 0;
+}
+
+inline static void sanitize(OMX_CONFIG_METADATAITEMTYPE *s) {
+ s->sLanguageCountry = 0;
+}
+
+inline static void sanitize(OMX_PARAM_PORTDEFINITIONTYPE *s) {
+ switch (s->eDomain) {
+ case OMX_PortDomainAudio:
+ s->format.audio.cMIMEType = 0;
+ break;
+ case OMX_PortDomainVideo:
+ s->format.video.cMIMEType = 0;
+ break;
+ case OMX_PortDomainImage:
+ s->format.image.cMIMEType = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+template <typename T>
+static bool sanitizeAs(void *setting, size_t size) {
+ T *s = asSetting<T>(setting, size);
+ if (s) {
+ sanitize(s);
+ return true;
+ }
+ return false;
+}
+
+static void sanitizeSetting(OMX_INDEXTYPE index, void *setting, size_t size) {
+ if (size < 8 || setting == nullptr) {
+ return;
+ }
+
+ bool ok = true;
+
+ // there are 3 standard OMX settings that contain pointer members
+ switch ((OMX_U32)index) {
+ case OMX_IndexConfigCounterNodeID:
+ ok = sanitizeAs<OMX_CONFIG_CONTAINERNODEIDTYPE>(setting, size);
+ break;
+ case OMX_IndexConfigMetadataItem:
+ ok = sanitizeAs<OMX_CONFIG_METADATAITEMTYPE>(setting, size);
+ break;
+ case OMX_IndexParamPortDefinition:
+ ok = sanitizeAs<OMX_PARAM_PORTDEFINITIONTYPE>(setting, size);
+ break;
+ }
+
+ if (!ok) {
+ // cannot effectively sanitize - we should not be here as IOMX.cpp
+ // should guard against size being too small. Nonetheless, log and
+ // clear result.
+ android_errorWriteLog(0x534e4554, "120781925");
+ memset(setting, 0, size);
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
// This provides the underlying Thread used by CallbackDispatcher.
@@ -608,7 +682,7 @@
}
status_t OMXNodeInstance::getParameter(
- OMX_INDEXTYPE index, void *params, size_t /* size */) {
+ OMX_INDEXTYPE index, void *params, size_t size) {
Mutex::Autolock autoLock(mLock);
if (mHandle == NULL) {
return DEAD_OBJECT;
@@ -625,6 +699,7 @@
if (err != OMX_ErrorNoMore) {
CLOG_IF_ERROR(getParameter, err, "%s(%#x)", asString(extIndex), index);
}
+ sanitizeSetting(index, params, size);
return StatusFromOMXError(err);
}
@@ -650,11 +725,12 @@
OMX_ERRORTYPE err = OMX_SetParameter(
mHandle, index, const_cast<void *>(params));
CLOG_IF_ERROR(setParameter, err, "%s(%#x)", asString(extIndex), index);
+ sanitizeSetting(index, const_cast<void *>(params), size);
return StatusFromOMXError(err);
}
status_t OMXNodeInstance::getConfig(
- OMX_INDEXTYPE index, void *params, size_t /* size */) {
+ OMX_INDEXTYPE index, void *params, size_t size) {
Mutex::Autolock autoLock(mLock);
if (mHandle == NULL) {
return DEAD_OBJECT;
@@ -671,6 +747,8 @@
if (err != OMX_ErrorNoMore) {
CLOG_IF_ERROR(getConfig, err, "%s(%#x)", asString(extIndex), index);
}
+
+ sanitizeSetting(index, params, size);
return StatusFromOMXError(err);
}
@@ -692,6 +770,7 @@
OMX_ERRORTYPE err = OMX_SetConfig(
mHandle, index, const_cast<void *>(params));
CLOG_IF_ERROR(setConfig, err, "%s(%#x)", asString(extIndex), index);
+ sanitizeSetting(index, const_cast<void *>(params), size);
return StatusFromOMXError(err);
}
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 7893148..039991c 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -211,6 +211,17 @@
status_t err = getPortDefinition(portIndex, &def);
EXPECT_SUCCESS(err, "getPortDefinition");
+ switch (def.eDomain) {
+ case OMX_PortDomainVideo:
+ EXPECT(def.format.video.cMIMEType == 0, "portDefinition video MIME");
+ break;
+ case OMX_PortDomainAudio:
+ EXPECT(def.format.audio.cMIMEType == 0, "portDefinition audio MIME");
+ break;
+ default:
+ break;
+ }
+
for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
Buffer buffer;
buffer.mFlags = 0;
diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp
index dd84bf2..d6d6e25 100644
--- a/services/audioflinger/FastCapture.cpp
+++ b/services/audioflinger/FastCapture.cpp
@@ -154,7 +154,7 @@
mReadBufferState = -1;
dumpState->mFrameCount = frameCount;
}
-
+ dumpState->mSilenced = current->mSilenceCapture;
}
void FastCapture::onWork()
@@ -208,6 +208,9 @@
mReadBufferState = frameCount;
}
if (mReadBufferState > 0) {
+ if (current->mSilenceCapture) {
+ memset(mReadBuffer, 0, mReadBufferState * Format_frameSize(mFormat));
+ }
ssize_t framesWritten = mPipeSink->write(mReadBuffer, mReadBufferState);
audio_track_cblk_t* cblk = current->mCblk;
if (fastPatchRecordBufferProvider != 0) {
diff --git a/services/audioflinger/FastCaptureDumpState.cpp b/services/audioflinger/FastCaptureDumpState.cpp
index 53eeba5..b8b3866 100644
--- a/services/audioflinger/FastCaptureDumpState.cpp
+++ b/services/audioflinger/FastCaptureDumpState.cpp
@@ -44,10 +44,11 @@
double periodSec = (double) mFrameCount / mSampleRate;
dprintf(fd, " FastCapture command=%s readSequence=%u framesRead=%u\n"
" readErrors=%u sampleRate=%u frameCount=%zu\n"
- " measuredWarmup=%.3g ms, warmupCycles=%u period=%.2f ms\n",
+ " measuredWarmup=%.3g ms, warmupCycles=%u period=%.2f ms\n"
+ " silenced: %s\n",
FastCaptureState::commandToString(mCommand), mReadSequence, mFramesRead,
mReadErrors, mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles,
- periodSec * 1e3);
+ periodSec * 1e3, mSilenced ? "true" : "false");
}
} // android
diff --git a/services/audioflinger/FastCaptureDumpState.h b/services/audioflinger/FastCaptureDumpState.h
index 6f9c4c3..a1b8706 100644
--- a/services/audioflinger/FastCaptureDumpState.h
+++ b/services/audioflinger/FastCaptureDumpState.h
@@ -35,6 +35,7 @@
uint32_t mReadErrors; // total number of read() errors
uint32_t mSampleRate;
size_t mFrameCount;
+ bool mSilenced = false; // capture is silenced
};
} // android
diff --git a/services/audioflinger/FastCaptureState.h b/services/audioflinger/FastCaptureState.h
index d287232..f949275 100644
--- a/services/audioflinger/FastCaptureState.h
+++ b/services/audioflinger/FastCaptureState.h
@@ -41,6 +41,8 @@
audio_format_t mFastPatchRecordFormat = AUDIO_FORMAT_INVALID;
AudioBufferProvider* mFastPatchRecordBufferProvider = nullptr; // a reference to a patch
// record in fast mode
+ bool mSilenceCapture = false; // request to silence capture for fast track.
+ // note: this also silences the normal mixer pipe
// Extends FastThreadState::Command
static const Command
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 4a4899f..024168b 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7089,6 +7089,8 @@
// reference to a fast track which is about to be removed
sp<RecordTrack> fastTrackToRemove;
+ bool silenceFastCapture = false;
+
{ // scope for mLock
Mutex::Autolock _l(mLock);
@@ -7176,14 +7178,33 @@
__func__, activeTrackState, activeTrack->id(), size);
}
- activeTracks.add(activeTrack);
- i++;
-
if (activeTrack->isFastTrack()) {
ALOG_ASSERT(!mFastTrackAvail);
ALOG_ASSERT(fastTrack == 0);
+ // if the active fast track is silenced either:
+ // 1) silence the whole capture from fast capture buffer if this is
+ // the only active track
+ // 2) invalidate this track: this will cause the client to reconnect and possibly
+ // be invalidated again until unsilenced
+ if (activeTrack->isSilenced()) {
+ if (size > 1) {
+ activeTrack->invalidate();
+ ALOG_ASSERT(fastTrackToRemove == 0);
+ fastTrackToRemove = activeTrack;
+ removeTrack_l(activeTrack);
+ mActiveTracks.remove(activeTrack);
+ size--;
+ continue;
+ } else {
+ silenceFastCapture = true;
+ }
+ }
fastTrack = activeTrack;
}
+
+ activeTracks.add(activeTrack);
+ i++;
+
}
mActiveTracks.updatePowerState(this);
@@ -7257,6 +7278,10 @@
AUDIO_FORMAT_INVALID : fastTrack->format();
didModify = true;
}
+ if (state->mSilenceCapture != silenceFastCapture) {
+ state->mSilenceCapture = silenceFastCapture;
+ didModify = true;
+ }
sq->end(didModify);
if (didModify) {
sq->push(block);
@@ -7341,8 +7366,10 @@
// Update server timestamp with server stats
// systemTime() is optional if the hardware supports timestamps.
- mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] += framesRead;
- mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = lastIoEndNs;
+ if (framesRead >= 0) {
+ mTimestamp.mPosition[ExtendedTimestamp::LOCATION_SERVER] += framesRead;
+ mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = lastIoEndNs;
+ }
// Update server timestamp with kernel stats
if (mPipeSource.get() == nullptr /* don't obtain for FastCapture, could block */) {
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 2a9a4c4..f05be06 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3820,7 +3820,11 @@
ALOGE("%s output not found for id %d", __func__, patch->sources[0].id);
return BAD_VALUE;
}
- // Reset handle so that setOutputDevice will force new AF patch to reach the sink
+ if (patchDesc->getHandle() != outputDesc->getPatchHandle()) {
+ // force SwOutput patch removal as AF counter part patch has already gone.
+ ALOGV("%s reset patch handle on Output as different from SWBridge", __func__);
+ removeAudioPatch(outputDesc->getPatchHandle());
+ }
outputDesc->setPatchHandle(AUDIO_PATCH_HANDLE_NONE);
setOutputDevices(outputDesc,
getNewOutputDevices(outputDesc, true /*fromCache*/),
diff --git a/services/mediametrics/statsd_codec.cpp b/services/mediametrics/statsd_codec.cpp
index 7a38c7c..26eda79 100644
--- a/services/mediametrics/statsd_codec.cpp
+++ b/services/mediametrics/statsd_codec.cpp
@@ -168,6 +168,28 @@
}
// android.media.mediacodec.latency.hist NOT EMITTED
+#if 0
+ // TODO(b/139143194)
+ // can't send them to statsd until statsd proto updates merge
+ // but in the meantime, they can appear in local 'dumpsys media.metrics' output
+ //
+ // android.media.mediacodec.bitrate_mode string
+ std::string bitrate_mode;
+ if (item->getString("android.media.mediacodec.bitrate_mode", &bitrate_mode)) {
+ metrics_proto.set_bitrate_mode(std::move(bitrate_mode));
+ }
+ // android.media.mediacodec.bitrate int32
+ int32_t bitrate = -1;
+ if (item->getInt32("android.media.mediacodec.bitrate", &bitrate)) {
+ metrics_proto.set_bitrate(bitrate);
+ }
+ // android.media.mediacodec.lifetimeMs int64
+ int64_t lifetimeMs = -1;
+ if ( item->getInt64("android.media.mediacodec.lifetimeMs", &lifetimeMs)) {
+ metrics_proto.set_lifetime_millis(lifetimeMs);
+ }
+#endif
+
std::string serialized;
if (!metrics_proto.SerializeToString(&serialized)) {
ALOGE("Failed to serialize codec metrics");
diff --git a/services/oboeservice/AAudioClientTracker.cpp b/services/oboeservice/AAudioClientTracker.cpp
index 6e14434..9d9ca63 100644
--- a/services/oboeservice/AAudioClientTracker.cpp
+++ b/services/oboeservice/AAudioClientTracker.cpp
@@ -106,18 +106,9 @@
aaudio_result_t
AAudioClientTracker::registerClientStream(pid_t pid, sp<AAudioServiceStreamBase> serviceStream) {
- aaudio_result_t result = AAUDIO_OK;
ALOGV("registerClientStream(%d,)\n", pid);
std::lock_guard<std::mutex> lock(mLock);
- sp<NotificationClient> notificationClient = mNotificationClients[pid];
- if (notificationClient == 0) {
- // This will get called the first time the audio server registers an internal stream.
- ALOGV("registerClientStream(%d,) unrecognized pid\n", pid);
- notificationClient = new NotificationClient(pid, nullptr);
- mNotificationClients[pid] = notificationClient;
- }
- notificationClient->registerClientStream(serviceStream);
- return result;
+ return getNotificationClient_l(pid)->registerClientStream(serviceStream);
}
// Find the tracker for this process and remove it.
@@ -136,6 +127,33 @@
return AAUDIO_OK;
}
+void AAudioClientTracker::setExclusiveEnabled(pid_t pid, bool enabled) {
+ ALOGD("%s(%d, %d)\n", __func__, pid, enabled);
+ std::lock_guard<std::mutex> lock(mLock);
+ getNotificationClient_l(pid)->setExclusiveEnabled(enabled);
+}
+
+bool AAudioClientTracker::isExclusiveEnabled(pid_t pid) {
+ std::lock_guard<std::mutex> lock(mLock);
+ return getNotificationClient_l(pid)->isExclusiveEnabled();
+}
+
+sp<AAudioClientTracker::NotificationClient>
+ AAudioClientTracker::getNotificationClient_l(pid_t pid) {
+ sp<NotificationClient> notificationClient = mNotificationClients[pid];
+ if (notificationClient == nullptr) {
+ // This will get called the first time the audio server uses this PID.
+ ALOGV("%s(%d,) unrecognized PID\n", __func__, pid);
+ notificationClient = new AAudioClientTracker::NotificationClient(pid, nullptr);
+ mNotificationClients[pid] = notificationClient;
+ }
+ return notificationClient;
+}
+
+// =======================================
+// AAudioClientTracker::NotificationClient
+// =======================================
+
AAudioClientTracker::NotificationClient::NotificationClient(pid_t pid, const sp<IBinder>& binder)
: mProcessId(pid), mBinder(binder) {
}
diff --git a/services/oboeservice/AAudioClientTracker.h b/services/oboeservice/AAudioClientTracker.h
index 00ff467..943b809 100644
--- a/services/oboeservice/AAudioClientTracker.h
+++ b/services/oboeservice/AAudioClientTracker.h
@@ -58,6 +58,15 @@
aaudio_result_t unregisterClientStream(pid_t pid,
android::sp<AAudioServiceStreamBase> serviceStream);
+ /**
+ * Specify whether a process is allowed to create an EXCLUSIVE MMAP stream.
+ * @param pid
+ * @param enabled
+ */
+ void setExclusiveEnabled(pid_t pid, bool enabled);
+
+ bool isExclusiveEnabled(pid_t pid);
+
android::AAudioService *getAAudioService() const {
return mAAudioService;
}
@@ -84,17 +93,29 @@
aaudio_result_t unregisterClientStream(android::sp<AAudioServiceStreamBase> serviceStream);
+ void setExclusiveEnabled(bool enabled) {
+ mExclusiveEnabled = enabled;
+ }
+
+ bool isExclusiveEnabled() {
+ return mExclusiveEnabled;
+ }
+
// IBinder::DeathRecipient
virtual void binderDied(const android::wp<IBinder>& who);
- protected:
+ private:
mutable std::mutex mLock;
const pid_t mProcessId;
std::set<android::sp<AAudioServiceStreamBase>> mStreams;
// hold onto binder to receive death notifications
android::sp<IBinder> mBinder;
+ bool mExclusiveEnabled = true;
};
+ // This must be called under mLock
+ android::sp<NotificationClient> getNotificationClient_l(pid_t pid);
+
mutable std::mutex mLock;
std::map<pid_t, android::sp<NotificationClient>> mNotificationClients;
android::AAudioService *mAAudioService = nullptr;
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index c9bf72f..9f34153 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -25,6 +25,7 @@
#include <sstream>
#include <utility/AAudioUtilities.h>
+#include "AAudioClientTracker.h"
#include "AAudioEndpointManager.h"
#include "AAudioServiceEndpointShared.h"
#include "AAudioServiceEndpointMMAP.h"
@@ -174,7 +175,15 @@
&& !request.isSharingModeMatchRequired()) { // app did not request a shared stream
ALOGD("%s() endpoint in EXCLUSIVE use. Steal it!", __func__);
mExclusiveStolenCount++;
- endpointToSteal = endpoint;
+ // Prevent this process from getting another EXCLUSIVE stream.
+ // This will prevent two clients from colliding after a DISCONNECTION
+ // when they both try to open an exclusive stream at the same time.
+ // That can result in a stream getting disconnected between the OPEN
+ // and START calls. This will help preserve app compatibility.
+ // An app can avoid having this happen by closing their streams when
+ // the app is paused.
+ AAudioClientTracker::getInstance().setExclusiveEnabled(request.getProcessId(), false);
+ endpointToSteal = endpoint; // return it to caller
}
return nullptr;
} else {
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 82b12d6..22cdb35 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -117,7 +117,8 @@
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
}
- if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+ if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE
+ && AAudioClientTracker::getInstance().isExclusiveEnabled(request.getProcessId())) {
// only trust audioserver for in service indication
bool inService = false;
if (isCallerInService()) {
diff --git a/tools/mainline_hook_partial.sh b/tools/mainline_hook_partial.sh
new file mode 100755
index 0000000..3dc6163
--- /dev/null
+++ b/tools/mainline_hook_partial.sh
Binary files differ
diff --git a/tools/mainline_hook_project.sh b/tools/mainline_hook_project.sh
new file mode 100755
index 0000000..8d35470
--- /dev/null
+++ b/tools/mainline_hook_project.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+#set -x
+
+# called for repo projects that are part of the media mainline modules
+# this is for projects where the entire project is part of mainline.
+# we have a separate script for projects where only part of that project gets
+# pulled into mainline.
+#
+# if the project's PREUPLOAD.cfg points to this script, it is by definition a project
+# which is entirely within mainline.
+#
+# example PREUPLOAD.cfg using this script
+# [Hook Scripts]
+# mainline_hook = ${REPO_ROOT}/frameworks/av/tools/mainline_hook_project.sh
+#
+
+
+# tunables
+DEV_BRANCH=rvc-dev
+
+###
+RED=$(tput setaf 1)
+NORMAL=$(tput sgr0)
+
+## check the active branch:
+## * b131183694 d198c6a [goog/master] Fix to handle missing checks on error returned
+##
+current=`git branch -vv | grep -P "^\*[^\[]+\[goog/"|sed -e 's/^.*\[//' | sed -e 's/:.*$//'| sed -e 's/^goog\///'`
+if [ "${current}" = "" ] ; then
+ current=unknown
+fi
+
+if [ "${current}" = "${DEV_BRANCH}" ] ; then
+ # Change appears to be in mainline dev branch
+ exit 0
+fi
+
+## warn the user that about not being on the typical/desired branch.
+
+cat - <<EOF
+
+You are uploading repo ${RED}${REPO_PATH}${NORMAL} to branch ${RED}${current}${NORMAL}.
+The source of truth for ${RED}${REPO_PATH}${NORMAL} is branch ${RED}${DEV_BRANCH}${NORMAL}.
+
+Please upload this change to branch ${RED}${DEV_BRANCH}${NORMAL} unless one or more of
+the following apply:
+- this is a security bug prohibited from disclosure before the next dessert release.
+ (moderate security bugs fall into this category).
+- this is new functionality prohibitied from disclosure before the next dessert release.
+EOF
+
+
+##
+## TODO: prompt the user y/n to continue right now instead of re-invoking with no-verify
+## this has to get around how repo buffers stdout from this script such that the output
+## is not flushed before we try to read the input.
+##
+
+cat - <<EOF
+If you are sure you want to proceed uploading to branch ${RED}${current}${NORMAL},
+re-run your repo upload command with the '--no-verify' option
+
+EOF
+exit 1
+