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#define LOG_TAG "AudioWatchdog"
18//#define LOG_NDEBUG 0
19
20#include "Configuration.h"
21#include <utils/Log.h>
22#include "AudioWatchdog.h"
23
24#ifdef AUDIO_WATCHDOG
25
26namespace android {
27
28void AudioWatchdogDump::dump(int fd)
29{
30    char buf[32];
31    if (mMostRecent != 0) {
32        // includes NUL terminator
33        ctime_r(&mMostRecent, buf);
34    } else {
35        strcpy(buf, "N/A\n");
36    }
37    dprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s",
38            mUnderruns, mLogs, buf);
39}
40
41bool AudioWatchdog::threadLoop()
42{
43    {
44        AutoMutex _l(mMyLock);
45        if (mPaused) {
46            mMyCond.wait(mMyLock);
47            // ignore previous timestamp after resume()
48            mOldTsValid = false;
49            // force an immediate log on first underrun after resume()
50            mLogTs.tv_sec = MIN_TIME_BETWEEN_LOGS_SEC;
51            mLogTs.tv_nsec = 0;
52            // caller will check for exitPending()
53            return true;
54        }
55    }
56    struct timespec newTs;
57    int rc = clock_gettime(CLOCK_MONOTONIC, &newTs);
58    if (rc != 0) {
59        pause();
60        return false;
61    }
62    if (!mOldTsValid) {
63        mOldTs = newTs;
64        mOldTsValid = true;
65        return true;
66    }
67    time_t sec = newTs.tv_sec - mOldTs.tv_sec;
68    long nsec = newTs.tv_nsec - mOldTs.tv_nsec;
69    if (nsec < 0) {
70        --sec;
71        nsec += 1000000000;
72    }
73    mOldTs = newTs;
74    // cycleNs is same as sec*1e9 + nsec, but limited to about 4 seconds
75    uint32_t cycleNs = nsec;
76    if (sec > 0) {
77        if (sec < 4) {
78            cycleNs += sec * 1000000000;
79        } else {
80            cycleNs = 4000000000u;
81        }
82    }
83    mLogTs.tv_sec += sec;
84    if ((mLogTs.tv_nsec += nsec) >= 1000000000) {
85        mLogTs.tv_sec++;
86        mLogTs.tv_nsec -= 1000000000;
87    }
88    if (cycleNs > mMaxCycleNs) {
89        mDump->mUnderruns = ++mUnderruns;
90        if (mLogTs.tv_sec >= MIN_TIME_BETWEEN_LOGS_SEC) {
91            mDump->mLogs = ++mLogs;
92            mDump->mMostRecent = time(NULL);
93            ALOGW("Insufficient CPU for load: expected=%.1f actual=%.1f ms; underruns=%u logs=%u",
94                mPeriodNs * 1e-6, cycleNs * 1e-6, mUnderruns, mLogs);
95            mLogTs.tv_sec = 0;
96            mLogTs.tv_nsec = 0;
97        }
98    }
99    struct timespec req;
100    req.tv_sec = 0;
101    req.tv_nsec = mPeriodNs;
102    rc = nanosleep(&req, NULL);
103    if (!((rc == 0) || (rc == -1 && errno == EINTR))) {
104        pause();
105        return false;
106    }
107    return true;
108}
109
110void AudioWatchdog::requestExit()
111{
112    // must be in this order to avoid a race condition
113    Thread::requestExit();
114    resume();
115}
116
117void AudioWatchdog::pause()
118{
119    AutoMutex _l(mMyLock);
120    mPaused = true;
121}
122
123void AudioWatchdog::resume()
124{
125    AutoMutex _l(mMyLock);
126    if (mPaused) {
127        mPaused = false;
128        mMyCond.signal();
129    }
130}
131
132void AudioWatchdog::setDump(AudioWatchdogDump *dump)
133{
134    mDump = dump != NULL ? dump : &mDummyDump;
135}
136
137}   // namespace android
138
139#endif // AUDIO_WATCHDOG
140