audio services: monitor execution time for binder commands
Add a mechanism to monitor execution time of incoming binder
calls to audio flinger and audio policy and cause native audioserver
restart in case of timeout.
Bug: 69250055
Test: manual. audio smoke tests
Change-Id: I01b5bf2599fb2a4cd265cbbe8d4e34b2b059aaf4
diff --git a/include/media/TimeCheck.h b/include/media/TimeCheck.h
new file mode 120000
index 0000000..e3ef134
--- /dev/null
+++ b/include/media/TimeCheck.h
@@ -0,0 +1 @@
+../../media/libmedia/include/media/TimeCheck.h
\ No newline at end of file
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 3358e35..873d836 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -24,6 +24,7 @@
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
+#include <media/TimeCheck.h>
#include <private/android_filesystem_config.h>
#include "IAudioFlinger.h"
@@ -933,6 +934,8 @@
break;
}
+ TimeCheck check("IAudioFlinger");
+
switch (code) {
case CREATE_TRACK: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index 8f5ff30..0b98502 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -27,6 +27,7 @@
#include <media/AudioEffect.h>
#include <media/IAudioPolicyService.h>
+#include <media/TimeCheck.h>
#include <private/android_filesystem_config.h>
#include <system/audio.h>
@@ -882,6 +883,8 @@
break;
}
+ TimeCheck check("IAudioPolicyService");
+
switch (code) {
case SET_DEVICE_CONNECTION_STATE: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 34b15a8..3990e69 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -18,7 +18,7 @@
vndk: {
enabled: true,
},
- srcs: ["AudioParameter.cpp", "TypeConverter.cpp"],
+ srcs: ["AudioParameter.cpp", "TypeConverter.cpp", "TimeCheck.cpp"],
cflags: [
"-Werror",
"-Wno-error=deprecated-declarations",
diff --git a/media/libmedia/TimeCheck.cpp b/media/libmedia/TimeCheck.cpp
new file mode 100644
index 0000000..dab5d4f
--- /dev/null
+++ b/media/libmedia/TimeCheck.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 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 <media/TimeCheck.h>
+
+namespace android {
+
+/* static */
+sp<TimeCheck::TimeCheckThread> TimeCheck::getTimeCheckThread()
+{
+ static sp<TimeCheck::TimeCheckThread> sTimeCheckThread = new TimeCheck::TimeCheckThread();
+ return sTimeCheckThread;
+}
+
+TimeCheck::TimeCheck(const char *tag, uint32_t timeoutMs)
+ : mEndTimeNs(getTimeCheckThread()->startMonitoring(tag, timeoutMs))
+{
+}
+
+TimeCheck::~TimeCheck() {
+ getTimeCheckThread()->stopMonitoring(mEndTimeNs);
+}
+
+TimeCheck::TimeCheckThread::~TimeCheckThread()
+{
+ AutoMutex _l(mMutex);
+ requestExit();
+ mMonitorRequests.clear();
+ mCond.signal();
+}
+
+nsecs_t TimeCheck::TimeCheckThread::startMonitoring(const char *tag, uint32_t timeoutMs) {
+ Mutex::Autolock _l(mMutex);
+ nsecs_t endTimeNs = systemTime() + milliseconds(timeoutMs);
+ for (; mMonitorRequests.indexOfKey(endTimeNs) >= 0; ++endTimeNs);
+ mMonitorRequests.add(endTimeNs, tag);
+ mCond.signal();
+ return endTimeNs;
+}
+
+void TimeCheck::TimeCheckThread::stopMonitoring(nsecs_t endTimeNs) {
+ Mutex::Autolock _l(mMutex);
+ mMonitorRequests.removeItem(endTimeNs);
+ mCond.signal();
+}
+
+bool TimeCheck::TimeCheckThread::threadLoop()
+{
+ status_t status = TIMED_OUT;
+ const char *tag;
+ {
+ AutoMutex _l(mMutex);
+
+ if (exitPending()) {
+ return false;
+ }
+
+ nsecs_t endTimeNs = INT64_MAX;
+ // KeyedVector mMonitorRequests is ordered so take first entry as next timeout
+ if (mMonitorRequests.size() != 0) {
+ endTimeNs = mMonitorRequests.keyAt(0);
+ tag = mMonitorRequests.valueAt(0);
+ }
+
+ const nsecs_t waitTimeNs = endTimeNs - systemTime();
+ if (waitTimeNs > 0) {
+ status = mCond.waitRelative(mMutex, waitTimeNs);
+ }
+ }
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "TimeCheck timeout for %s", tag);
+ return true;
+}
+
+}; // namespace android
diff --git a/media/libmedia/include/media/TimeCheck.h b/media/libmedia/include/media/TimeCheck.h
new file mode 100644
index 0000000..6c5f656
--- /dev/null
+++ b/media/libmedia/include/media/TimeCheck.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 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 ANDROID_TIME_CHECK_H
+#define ANDROID_TIME_CHECK_H
+
+#include <utils/KeyedVector.h>
+#include <utils/Thread.h>
+
+
+namespace android {
+
+// A class monitoring execution time for a code block (scoped variable) and causing an assert
+// if it exceeds a certain time
+
+class TimeCheck {
+public:
+
+ // The default timeout is chosen to be less than system server watchdog timeout
+ static constexpr uint32_t kDefaultTimeOutMs = 5000;
+
+ TimeCheck(const char *tag, uint32_t timeoutMs = kDefaultTimeOutMs);
+ ~TimeCheck();
+
+private:
+
+ class TimeCheckThread : public Thread {
+ public:
+
+ TimeCheckThread() {}
+ virtual ~TimeCheckThread() override;
+
+ nsecs_t startMonitoring(const char *tag, uint32_t timeoutMs);
+ void stopMonitoring(nsecs_t endTimeNs);
+
+ private:
+
+ // RefBase
+ virtual void onFirstRef() override { run("TimeCheckThread", PRIORITY_URGENT_AUDIO); }
+
+ // Thread
+ virtual bool threadLoop() override;
+
+ Condition mCond;
+ Mutex mMutex;
+ // using the end time in ns as key is OK given the risk is low that two entries
+ // are added in such a way that <add time> + <timeout> are the same for both.
+ KeyedVector< nsecs_t, const char*> mMonitorRequests;
+ };
+
+ static sp<TimeCheckThread> getTimeCheckThread();
+
+ const nsecs_t mEndTimeNs;
+};
+
+}; // namespace android
+
+#endif // ANDROID_TIME_CHECK_H