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