Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 43b272e2 authored by Adam Bookatz's avatar Adam Bookatz Committed by Android (Google) Code Review
Browse files

Merge changes I82fe220e,Iff324c73,I3bd73acd

* changes:
  indexed priority queue for AnomalyMonitor
  StatsCompanionService connection to Statsd
  Set up StatsCompanionService.java
parents 8c85e5ff 0e95909e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -270,6 +270,7 @@ LOCAL_SRC_FILES += \
	core/java/android/os/IRecoverySystemProgressListener.aidl \
	core/java/android/os/IRemoteCallback.aidl \
	core/java/android/os/ISchedulingPolicyService.aidl \
	core/java/android/os/IStatsCompanionService.aidl \
	core/java/android/os/IStatsManager.aidl \
	core/java/android/os/IThermalEventListener.aidl \
	core/java/android/os/IThermalService.aidl \
+3 −0
Original line number Diff line number Diff line
@@ -41,7 +41,9 @@ LOCAL_MODULE := statsd

LOCAL_SRC_FILES := \
    ../../core/java/android/os/IStatsManager.aidl \
    ../../core/java/android/os/IStatsCompanionService.aidl \
    src/StatsService.cpp \
    src/AnomalyMonitor.cpp \
    src/LogEntryPrinter.cpp \
    src/LogReader.cpp \
    src/main.cpp \
@@ -119,6 +121,7 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH)/src \
LOCAL_SRC_FILES := \
    ../../core/java/android/os/IStatsManager.aidl \
    src/StatsService.cpp \
    tests/indexed_priority_queue_test.cpp \
    src/LogEntryPrinter.cpp \
    src/LogReader.cpp \
    tests/LogReader_test.cpp \
+111 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.
 */

#define LOG_TAG "AnomalyMonitor"
#define DEBUG true

#include <AnomalyMonitor.h>

#include <binder/IServiceManager.h>
#include <cutils/log.h>

namespace statsd {

AnomalyMonitor::AnomalyMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec)
        : mRegisteredAlarmTimeSec(0),
          mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec) {
}

AnomalyMonitor::~AnomalyMonitor() {
}

void AnomalyMonitor::add(sp<const AnomalyAlarm> alarm) {
    if (alarm == nullptr) {
        ALOGW("Asked to add a null alarm.");
        return;
    }
    if (alarm->timestampSec < 1) {
        // forbidden since a timestamp 0 is used to indicate no alarm registered
        ALOGW("Asked to add a 0-time alarm.");
        return;
    }
    std::lock_guard<std::mutex> lock(mLock);
    // TODO: Ensure that refractory period is respected.
    if (DEBUG) ALOGD("Adding alarm with time %u", alarm->timestampSec);
    mPq.push(alarm);
    if (mRegisteredAlarmTimeSec < 1 ||
            alarm->timestampSec + mMinUpdateTimeSec < mRegisteredAlarmTimeSec) {
        updateRegisteredAlarmTime(alarm->timestampSec);
    }
}

void AnomalyMonitor::remove(sp<const AnomalyAlarm> alarm) {
    if (alarm == nullptr) {
        ALOGW("Asked to remove a null alarm.");
        return;
    }
    std::lock_guard<std::mutex> lock(mLock);
    if (DEBUG) ALOGD("Removing alarm with time %u", alarm->timestampSec);
    mPq.remove(alarm);
    if (mPq.empty()) {
        if (DEBUG) ALOGD("Queue is empty. Cancel any alarm.");
        mRegisteredAlarmTimeSec = 0;
        // TODO: Make this resistant to doing work when companion is not ready yet
        sp<IStatsCompanionService> statsCompanionService = getStatsCompanion_l();
        if (statsCompanionService != nullptr) {
            statsCompanionService->cancelAnomalyAlarm();
        }
        return;
    }
    uint32_t soonestAlarmTimeSec = mPq.top()->timestampSec;
    if (DEBUG) ALOGD("Soonest alarm is %u", soonestAlarmTimeSec);
    if (soonestAlarmTimeSec > mRegisteredAlarmTimeSec + mMinUpdateTimeSec) {
        updateRegisteredAlarmTime(soonestAlarmTimeSec);
    }
}

void AnomalyMonitor::updateRegisteredAlarmTime(uint32_t timestampSec) {
    if (DEBUG) ALOGD("Updating reg alarm time to %u", timestampSec);
    mRegisteredAlarmTimeSec = timestampSec;
    sp<IStatsCompanionService> statsCompanionService = getStatsCompanion_l();
    if (statsCompanionService != nullptr) {
        statsCompanionService->setAnomalyAlarm(secToMs(mRegisteredAlarmTimeSec));
    }
}

sp<IStatsCompanionService> AnomalyMonitor::getStatsCompanion_l() {
    if (mStatsCompanion != nullptr) {
        return mStatsCompanion;
    }
    // Get statscompanion service from service manager
    const sp<IServiceManager> sm(defaultServiceManager());
    if (sm != nullptr) {
        const String16 name("statscompanion");
        mStatsCompanion =
                interface_cast<IStatsCompanionService>(sm->checkService(name));
        if (mStatsCompanion == nullptr) {
            ALOGW("statscompanion service unavailable!");
            return nullptr;
        }
    }
    return mStatsCompanion;
}

int64_t AnomalyMonitor::secToMs(uint32_t timeSec) {
    return ((int64_t) timeSec) * 1000;
}

}  // namespace statsd
 No newline at end of file
+129 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 ANOMALY_MONITOR_H
#define ANOMALY_MONITOR_H

#include <indexed_priority_queue.h>
#include <android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>

#include <queue>
#include <vector>

using namespace android::os;
using namespace android;

namespace statsd {

/**
 * Represents an alarm, associated with some aggregate metric, holding a
 * projected time at which the metric is expected to exceed its anomaly
 * threshold.
 * Timestamps are in seconds since epoch in a uint32, so will fail in year 2106.
 */
struct AnomalyAlarm : public RefBase {
    AnomalyAlarm(uint32_t timestampSec) : timestampSec(timestampSec) {
    }

    const uint32_t timestampSec;

    /** AnomalyAlarm a is smaller (higher priority) than b if its timestamp is sooner. */
    struct SmallerTimestamp {
        bool operator()(sp<const AnomalyAlarm> a, sp<const AnomalyAlarm> b) const {
            return (a->timestampSec < b->timestampSec);
        }
    };
};

/**
 * Manages alarms for Anomaly Detection.
 */
class AnomalyMonitor {
 public:
    /**
     * @param minDiffToUpdateRegisteredAlarmTimeSec If the soonest alarm differs
     * from the registered alarm by more than this amount, update the registered
     * alarm.
     */
    AnomalyMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec);
    ~AnomalyMonitor();

    /**
     * Adds the given alarm (reference) to the queue.
     */
    void add(sp<const AnomalyAlarm> alarm);

    /**
     * Removes the given alarm (reference) from the queue.
     * Note that alarm comparison is reference-based; if another alarm exists
     * with the same timestampSec, that alarm will still remain in the queue.
     */
    void remove(sp<const AnomalyAlarm> alarm);

    /**
     * Returns the projected alarm timestamp that is registered with
     * StatsCompanionService. This may not be equal to the soonest alarm,
     * but should be within minDiffToUpdateRegisteredAlarmTimeSec of it.
     */
    uint32_t getRegisteredAlarmTimeSec() const {
        return mRegisteredAlarmTimeSec;
    }

 private:
    /** Lock for accessing/writing to mPq. */
    std::mutex mLock;

    /**
     * Timestamp (seconds since epoch) of the alarm registered with
     * StatsCompanionService. This, in general, may not be equal to the soonest
     * alarm stored in mPq, but should be within minUpdateTimeSec of it.
     * A value of 0 indicates that no alarm is currently registered.
     */
    uint32_t mRegisteredAlarmTimeSec;

    /**
     * Priority queue of alarms, prioritized by soonest alarm.timestampSec.
     */
    indexed_priority_queue<AnomalyAlarm, AnomalyAlarm::SmallerTimestamp> mPq;

    /**
     * Binder interface for communicating with StatsCompanionService.
     */
    sp<IStatsCompanionService> mStatsCompanion;

    /**
     * Amount by which the soonest projected alarm must differ from
     * mRegisteredAlarmTimeSec before updateRegisteredAlarmTime is called.
     */
    uint32_t mMinUpdateTimeSec;

    /**
     * Updates the alarm registered with StatsCompanionService to the given time.
     * Also correspondingly updates mRegisteredAlarmTimeSec.
     */
    void updateRegisteredAlarmTime(uint32_t timestampSec);

    /** Returns the StatsCompanionService. */
    sp<IStatsCompanionService> getStatsCompanion_l();

    /** Converts uint32 timestamp in seconds to a Java long in msec. */
    int64_t secToMs(uint32_t timeSec);
};

} // namespace statsd

#endif // ANOMALY_MONITOR_H
 No newline at end of file
+195 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 STATSD_INDEXED_PRIORITY_QUEUE_H
#define STATSD_INDEXED_PRIORITY_QUEUE_H

// ALOGE can be called from this file. If header loaded by another class, use their LOG_TAG instead.
#ifndef LOG_TAG
#define LOG_TAG "statsd(indexed_priority_queue)"
#endif //LOG_TAG

#include <cutils/log.h>
#include <unordered_map>
#include <utils/RefBase.h>
#include <vector>

using namespace android;

namespace statsd {

/** Defines a hash function for sp<AA>, returning the hash of the underlying pointer. */
template <class AA>
struct SpHash {
    size_t operator()(const sp<const AA>& k) const {
        return std::hash<const AA*>()(k.get());
    }
};

/**
 * Min priority queue for generic type AA.
 * Unlike a regular priority queue, this class is also capable of removing interior elements.
 * @tparam Comparator must implement [bool operator()(sp<const AA> a, sp<const AA> b)], returning
 *    whether a should be closer to the top of the queue than b.
 */
template <class AA, class Comparator>
class indexed_priority_queue {
 public:
    indexed_priority_queue();
    /** Adds a into the priority queue. If already present or a==nullptr, does nothing. */
    void push(sp<const AA> a);
    /** Removes a from the priority queue. If not present or a==nullptr, does nothing. */
    void remove(sp<const AA> a);
    /** Removes all elements. */
    void clear();
    /** Returns whether priority queue contains a (not just a copy of a, but a itself). */
    bool contains(sp<const AA> a) const;
    /** Returns min element. Returns nullptr iff empty(). */
    sp<const AA> top() const;
    /** Returns number of elements in priority queue. */
    size_t size() const { return pq.size() - 1; } // pq is 1-indexed
    /** Returns true iff priority queue is empty. */
    bool empty() const { return size() < 1; }

 private:
    /** Vector representing a min-heap (1-indexed, with nullptr at 0). */
    std::vector<sp<const AA>> pq;
    /** Mapping of each element in pq to its index in pq (i.e. the inverse of a=pq[i]). */
    std::unordered_map<sp<const AA>, size_t, SpHash<AA>> indices;

    void init();
    void sift_up(size_t idx);
    void sift_down(size_t idx);
    /** Returns whether pq[idx1] is considered higher than pq[idx2], according to Comparator. */
    bool higher(size_t idx1, size_t idx2) const;
    void swap_indices(size_t i, size_t j);
};

// Implementation must be done in this file due to use of template.

template <class AA, class Comparator>
indexed_priority_queue<AA,Comparator>::indexed_priority_queue() {
    init();
}

template <class AA, class Comparator>
void indexed_priority_queue<AA,Comparator>::push(sp<const AA> a) {
    if (a == nullptr) return;
    if (contains(a)) return;
    pq.push_back(a);
    size_t idx = size(); // index of last element since 1-indexed
    indices.insert({a, idx});
    sift_up(idx); // get the pq back in order
}

template <class AA, class Comparator>
void indexed_priority_queue<AA,Comparator>::remove(sp<const AA> a) {
    if (a == nullptr) return;
    if (!contains(a)) return;
    size_t idx = indices[a];
    if (idx >= pq.size()) {
        ALOGE("indexed_priority_queue: Invalid index in map of indices.");
        return;
    }
    if (idx == size()) { // if a is the last element, i.e. at index idx == size() == (pq.size()-1)
        pq.pop_back();
        indices.erase(a);
        return;
    }
    // move last element (guaranteed not to be at idx) to idx, then delete a
    sp<const AA> last_a = pq.back();
    pq[idx] = last_a;
    pq.pop_back();
    indices[last_a] = idx;
    indices.erase(a);

    // get the heap back in order (since the element at idx is not in order)
    sift_up(idx);
    sift_down(idx);
}

template <class AA, class Comparator>
void indexed_priority_queue<AA,Comparator>::clear() {
    pq.clear();
    indices.clear();
    init();
}

template <class AA, class Comparator>
sp<const AA> indexed_priority_queue<AA,Comparator>::top() const {
    if (empty()) return nullptr;
    return pq[1];
}

template <class AA, class Comparator>
void indexed_priority_queue<AA,Comparator>::init() {
    pq.push_back(nullptr); // so that pq is 1-indexed.
    indices.insert({nullptr, 0}); // just to be consistent with pq.
}

template <class AA, class Comparator>
void indexed_priority_queue<AA,Comparator>::sift_up(size_t idx) {
    while (idx > 1) {
        size_t parent = idx/2;
        if (higher(idx, parent)) swap_indices(idx, parent);
        else break;
        idx = parent;
    }
}

template <class AA, class Comparator>
void indexed_priority_queue<AA,Comparator>::sift_down(size_t idx) {
    while (2*idx <= size()) {
        size_t child = 2 * idx;
        if (child < size() && higher(child+1, child)) child++;
        if (higher(child, idx)) swap_indices(child, idx);
        else break;
        idx = child;
    }
}

template <class AA, class Comparator>
bool indexed_priority_queue<AA,Comparator>::higher(size_t idx1, size_t idx2) const {
    if (!(0u < idx1 && idx1 < pq.size() && 0u < idx2 && idx2 < pq.size())) {
        ALOGE("indexed_priority_queue: Attempting to access invalid index");
        return false; // got to do something.
    }
    return Comparator()(pq[idx1], pq[idx2]);
}

template <class AA, class Comparator>
bool indexed_priority_queue<AA,Comparator>::contains(sp<const AA> a) const {
    if (a == nullptr) return false; // publicly, we pretend that nullptr is not actually in pq.
    return indices.count(a) > 0;
}

template <class AA, class Comparator>
void indexed_priority_queue<AA,Comparator>::swap_indices(size_t i, size_t j) {
    if (!(0u < i && i < pq.size() && 0u < j && j < pq.size())) {
        ALOGE("indexed_priority_queue: Attempting to swap invalid index");
        return;
    }
    sp<const AA> val_i = pq[i];
    sp<const AA> val_j = pq[j];
    pq[i] = val_j;
    pq[j] = val_i;
    indices[val_i] = j;
    indices[val_j] = i;
}

} // namespace statsd

#endif //STATSD_INDEXED_PRIORITY_QUEUE_H
Loading