1/*
2 * Copyright (C) 2017 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 DEBUG false
18#include "Log.h"
19
20#include "anomaly/AlarmMonitor.h"
21#include "guardrail/StatsdStats.h"
22
23namespace android {
24namespace os {
25namespace statsd {
26
27AlarmMonitor::AlarmMonitor(
28        uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
29        const std::function<void(const sp<IStatsCompanionService>&, int64_t)>& updateAlarm,
30        const std::function<void(const sp<IStatsCompanionService>&)>& cancelAlarm)
31    : mRegisteredAlarmTimeSec(0), mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec),
32      mUpdateAlarm(updateAlarm),
33      mCancelAlarm(cancelAlarm) {}
34
35AlarmMonitor::~AlarmMonitor() {}
36
37void AlarmMonitor::setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
38    std::lock_guard<std::mutex> lock(mLock);
39    sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
40    mStatsCompanionService = statsCompanionService;
41    if (statsCompanionService == nullptr) {
42        VLOG("Erasing link to statsCompanionService");
43        return;
44    }
45    VLOG("Creating link to statsCompanionService");
46    const sp<const InternalAlarm> top = mPq.top();
47    if (top != nullptr) {
48        updateRegisteredAlarmTime_l(top->timestampSec);
49    }
50}
51
52void AlarmMonitor::add(sp<const InternalAlarm> alarm) {
53    std::lock_guard<std::mutex> lock(mLock);
54    if (alarm == nullptr) {
55        ALOGW("Asked to add a null alarm.");
56        return;
57    }
58    if (alarm->timestampSec < 1) {
59        // forbidden since a timestamp 0 is used to indicate no alarm registered
60        ALOGW("Asked to add a 0-time alarm.");
61        return;
62    }
63    // TODO: Ensure that refractory period is respected.
64    VLOG("Adding alarm with time %u", alarm->timestampSec);
65    mPq.push(alarm);
66    if (mRegisteredAlarmTimeSec < 1 ||
67        alarm->timestampSec + mMinUpdateTimeSec < mRegisteredAlarmTimeSec) {
68        updateRegisteredAlarmTime_l(alarm->timestampSec);
69    }
70}
71
72void AlarmMonitor::remove(sp<const InternalAlarm> alarm) {
73    std::lock_guard<std::mutex> lock(mLock);
74    if (alarm == nullptr) {
75        ALOGW("Asked to remove a null alarm.");
76        return;
77    }
78    VLOG("Removing alarm with time %u", alarm->timestampSec);
79    bool wasPresent = mPq.remove(alarm);
80    if (!wasPresent) return;
81    if (mPq.empty()) {
82        VLOG("Queue is empty. Cancel any alarm.");
83        cancelRegisteredAlarmTime_l();
84        return;
85    }
86    uint32_t soonestAlarmTimeSec = mPq.top()->timestampSec;
87    VLOG("Soonest alarm is %u", soonestAlarmTimeSec);
88    if (soonestAlarmTimeSec > mRegisteredAlarmTimeSec + mMinUpdateTimeSec) {
89        updateRegisteredAlarmTime_l(soonestAlarmTimeSec);
90    }
91}
92
93// More efficient than repeatedly calling remove(mPq.top()) since it batches the
94// updates to the registered alarm.
95unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> AlarmMonitor::popSoonerThan(
96        uint32_t timestampSec) {
97    VLOG("Removing alarms with time <= %u", timestampSec);
98    unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> oldAlarms;
99    std::lock_guard<std::mutex> lock(mLock);
100
101    for (sp<const InternalAlarm> t = mPq.top(); t != nullptr && t->timestampSec <= timestampSec;
102        t = mPq.top()) {
103        oldAlarms.insert(t);
104        mPq.pop();  // remove t
105    }
106    // Always update registered alarm time (if anything has changed).
107    if (!oldAlarms.empty()) {
108        if (mPq.empty()) {
109            VLOG("Queue is empty. Cancel any alarm.");
110            cancelRegisteredAlarmTime_l();
111        } else {
112            // Always update the registered alarm in this case (unlike remove()).
113            updateRegisteredAlarmTime_l(mPq.top()->timestampSec);
114        }
115    }
116    return oldAlarms;
117}
118
119void AlarmMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) {
120    VLOG("Updating reg alarm time to %u", timestampSec);
121    mRegisteredAlarmTimeSec = timestampSec;
122    mUpdateAlarm(mStatsCompanionService, secToMs(mRegisteredAlarmTimeSec));
123}
124
125void AlarmMonitor::cancelRegisteredAlarmTime_l() {
126    VLOG("Cancelling reg alarm.");
127    mRegisteredAlarmTimeSec = 0;
128    mCancelAlarm(mStatsCompanionService);
129}
130
131int64_t AlarmMonitor::secToMs(uint32_t timeSec) {
132    return ((int64_t)timeSec) * 1000;
133}
134
135}  // namespace statsd
136}  // namespace os
137}  // namespace android
138