Merge changes I82fe220e,Iff324c73,I3bd73acd
* changes: indexed priority queue for AnomalyMonitor StatsCompanionService connection to Statsd Set up StatsCompanionService.java
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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
cmds/statsd/src/AnomalyMonitor.cpp
Normal file
111
cmds/statsd/src/AnomalyMonitor.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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
|
||||
129
cmds/statsd/src/AnomalyMonitor.h
Normal file
129
cmds/statsd/src/AnomalyMonitor.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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
|
||||
195
cmds/statsd/src/indexed_priority_queue.h
Normal file
195
cmds/statsd/src/indexed_priority_queue.h
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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
|
||||
188
cmds/statsd/tests/indexed_priority_queue_test.cpp
Normal file
188
cmds/statsd/tests/indexed_priority_queue_test.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "../src/indexed_priority_queue.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace statsd;
|
||||
|
||||
/** struct for template in indexed_priority_queue */
|
||||
struct AATest : public RefBase {
|
||||
AATest(uint32_t val) : val(val) {
|
||||
}
|
||||
|
||||
const int val;
|
||||
|
||||
struct Smaller {
|
||||
bool operator()(const sp<const AATest> a, const sp<const AATest> b) const {
|
||||
return (a->val < b->val);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
#ifdef __ANDROID__
|
||||
TEST(indexed_priority_queue, empty_and_size) {
|
||||
indexed_priority_queue<AATest, AATest::Smaller> ipq;
|
||||
sp<const AATest> aa4 = new AATest{4};
|
||||
sp<const AATest> aa8 = new AATest{8};
|
||||
|
||||
EXPECT_EQ(0u, ipq.size());
|
||||
EXPECT_TRUE(ipq.empty());
|
||||
|
||||
ipq.push(aa4);
|
||||
EXPECT_EQ(1u, ipq.size());
|
||||
EXPECT_FALSE(ipq.empty());
|
||||
|
||||
ipq.push(aa8);
|
||||
EXPECT_EQ(2u, ipq.size());
|
||||
EXPECT_FALSE(ipq.empty());
|
||||
|
||||
ipq.remove(aa4);
|
||||
EXPECT_EQ(1u, ipq.size());
|
||||
EXPECT_FALSE(ipq.empty());
|
||||
|
||||
ipq.remove(aa8);
|
||||
EXPECT_EQ(0u, ipq.size());
|
||||
EXPECT_TRUE(ipq.empty());
|
||||
}
|
||||
|
||||
TEST(indexed_priority_queue, top) {
|
||||
indexed_priority_queue<AATest, AATest::Smaller> ipq;
|
||||
sp<const AATest> aa2 = new AATest{2};
|
||||
sp<const AATest> aa4 = new AATest{4};
|
||||
sp<const AATest> aa8 = new AATest{8};
|
||||
sp<const AATest> aa12 = new AATest{12};
|
||||
sp<const AATest> aa16 = new AATest{16};
|
||||
sp<const AATest> aa20 = new AATest{20};
|
||||
|
||||
EXPECT_EQ(ipq.top(), nullptr);
|
||||
|
||||
// add 8, 4, 12
|
||||
ipq.push(aa8);
|
||||
EXPECT_EQ(ipq.top(), aa8);
|
||||
|
||||
ipq.push(aa12);
|
||||
EXPECT_EQ(ipq.top(), aa8);
|
||||
|
||||
ipq.push(aa4);
|
||||
EXPECT_EQ(ipq.top(), aa4);
|
||||
|
||||
// remove 12, 4
|
||||
ipq.remove(aa12);
|
||||
EXPECT_EQ(ipq.top(), aa4);
|
||||
|
||||
ipq.remove(aa4);
|
||||
EXPECT_EQ(ipq.top(), aa8);
|
||||
|
||||
// add 16, 2, 20
|
||||
ipq.push(aa16);
|
||||
EXPECT_EQ(ipq.top(), aa8);
|
||||
|
||||
ipq.push(aa2);
|
||||
EXPECT_EQ(ipq.top(), aa2);
|
||||
|
||||
ipq.push(aa20);
|
||||
EXPECT_EQ(ipq.top(), aa2);
|
||||
|
||||
// remove 2, 20, 16, 8
|
||||
ipq.remove(aa2);
|
||||
EXPECT_EQ(ipq.top(), aa8);
|
||||
|
||||
ipq.remove(aa20);
|
||||
EXPECT_EQ(ipq.top(), aa8);
|
||||
|
||||
ipq.remove(aa16);
|
||||
EXPECT_EQ(ipq.top(), aa8);
|
||||
|
||||
ipq.remove(aa8);
|
||||
EXPECT_EQ(ipq.top(), nullptr);
|
||||
}
|
||||
|
||||
TEST(indexed_priority_queue, push_same_aa) {
|
||||
indexed_priority_queue<AATest, AATest::Smaller> ipq;
|
||||
sp<const AATest> aa4_a = new AATest{4};
|
||||
sp<const AATest> aa4_b = new AATest{4};
|
||||
|
||||
ipq.push(aa4_a);
|
||||
EXPECT_EQ(1u, ipq.size());
|
||||
EXPECT_TRUE(ipq.contains(aa4_a));
|
||||
EXPECT_FALSE(ipq.contains(aa4_b));
|
||||
|
||||
ipq.push(aa4_a);
|
||||
EXPECT_EQ(1u, ipq.size());
|
||||
EXPECT_TRUE(ipq.contains(aa4_a));
|
||||
EXPECT_FALSE(ipq.contains(aa4_b));
|
||||
|
||||
ipq.push(aa4_b);
|
||||
EXPECT_EQ(2u, ipq.size());
|
||||
EXPECT_TRUE(ipq.contains(aa4_a));
|
||||
EXPECT_TRUE(ipq.contains(aa4_b));
|
||||
}
|
||||
|
||||
|
||||
TEST(indexed_priority_queue, remove_nonexistant) {
|
||||
indexed_priority_queue<AATest, AATest::Smaller> ipq;
|
||||
sp<const AATest> aa4 = new AATest{4};
|
||||
sp<const AATest> aa5 = new AATest{5};
|
||||
|
||||
ipq.push(aa4);
|
||||
ipq.remove(aa5);
|
||||
EXPECT_EQ(1u, ipq.size());
|
||||
EXPECT_TRUE(ipq.contains(aa4));
|
||||
EXPECT_FALSE(ipq.contains(aa5));
|
||||
}
|
||||
|
||||
TEST(indexed_priority_queue, remove_same_aa) {
|
||||
indexed_priority_queue<AATest, AATest::Smaller> ipq;
|
||||
sp<const AATest> aa4_a = new AATest{4};
|
||||
sp<const AATest> aa4_b = new AATest{4};
|
||||
|
||||
ipq.push(aa4_a);
|
||||
ipq.push(aa4_b);
|
||||
EXPECT_EQ(2u, ipq.size());
|
||||
EXPECT_TRUE(ipq.contains(aa4_a));
|
||||
EXPECT_TRUE(ipq.contains(aa4_b));
|
||||
|
||||
ipq.remove(aa4_b);
|
||||
EXPECT_EQ(1u, ipq.size());
|
||||
EXPECT_TRUE(ipq.contains(aa4_a));
|
||||
EXPECT_FALSE(ipq.contains(aa4_b));
|
||||
|
||||
ipq.remove(aa4_a);
|
||||
EXPECT_EQ(0u, ipq.size());
|
||||
EXPECT_FALSE(ipq.contains(aa4_a));
|
||||
EXPECT_FALSE(ipq.contains(aa4_b));
|
||||
}
|
||||
|
||||
TEST(indexed_priority_queue, nulls) {
|
||||
indexed_priority_queue<AATest, AATest::Smaller> ipq;
|
||||
|
||||
EXPECT_TRUE(ipq.empty());
|
||||
EXPECT_FALSE(ipq.contains(nullptr));
|
||||
|
||||
ipq.push(nullptr);
|
||||
EXPECT_TRUE(ipq.empty());
|
||||
EXPECT_FALSE(ipq.contains(nullptr));
|
||||
|
||||
ipq.remove(nullptr);
|
||||
EXPECT_TRUE(ipq.empty());
|
||||
EXPECT_FALSE(ipq.contains(nullptr));
|
||||
}
|
||||
|
||||
#else
|
||||
GTEST_LOG_(INFO) << "This test does nothing.\n";
|
||||
#endif
|
||||
@@ -2991,6 +2991,7 @@ public abstract class Context {
|
||||
//@hide: CONTEXTHUB_SERVICE,
|
||||
SYSTEM_HEALTH_SERVICE,
|
||||
//@hide: INCIDENT_SERVICE,
|
||||
//@hide: STATS_COMPANION_SERVICE,
|
||||
COMPANION_DEVICE_SERVICE
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@@ -4019,6 +4020,12 @@ public abstract class Context {
|
||||
*/
|
||||
public static final String INCIDENT_SERVICE = "incident";
|
||||
|
||||
/**
|
||||
* Service to assist statsd in obtaining general stats.
|
||||
* @hide
|
||||
*/
|
||||
public static final String STATS_COMPANION_SERVICE = "statscompanion";
|
||||
|
||||
/**
|
||||
* Use with {@link #getSystemService} to retrieve a {@link
|
||||
* android.content.om.OverlayManager} for managing overlay packages.
|
||||
|
||||
44
core/java/android/os/IStatsCompanionService.aidl
Normal file
44
core/java/android/os/IStatsCompanionService.aidl
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package android.os;
|
||||
|
||||
/**
|
||||
* Binder interface to communicate with the Java-based statistics service helper.
|
||||
* {@hide}
|
||||
*/
|
||||
interface IStatsCompanionService {
|
||||
/**
|
||||
* Register an alarm for anomaly detection to fire at the given timestamp (ms since epoch).
|
||||
* If anomaly alarm had already been registered, it will be replaced with the new timestamp.
|
||||
* Uses AlarmManager.set API, so if the timestamp is in the past, alarm fires immediately, and
|
||||
* alarm is inexact.
|
||||
*/
|
||||
void setAnomalyAlarm(long timestampMs);
|
||||
/** Cancel any anomaly detection alarm. */
|
||||
void cancelAnomalyAlarm();
|
||||
|
||||
/**
|
||||
* Register a repeating alarm for polling to fire at the given timestamp and every
|
||||
* intervalMs thereafter (in ms since epoch).
|
||||
* If polling alarm had already been registered, it will be replaced by new one.
|
||||
* Uses AlarmManager.setRepeating API, so if the timestamp is in past, alarm fires immediately,
|
||||
* and alarm is inexact.
|
||||
*/
|
||||
void setPollingAlarms(long timestampMs, long intervalMs);
|
||||
/** Cancel any repeating polling alarm. */
|
||||
void cancelPollingAlarms();
|
||||
}
|
||||
@@ -3074,6 +3074,12 @@
|
||||
<permission android:name="android.permission.BATTERY_STATS"
|
||||
android:protectionLevel="signature|privileged|development" />
|
||||
|
||||
<!--Allows an application to manage statscompanion.
|
||||
<p>Not for use by third-party applications.
|
||||
@hide -->
|
||||
<permission android:name="android.permission.STATSCOMPANION"
|
||||
android:protectionLevel="signature" />
|
||||
|
||||
<!-- @SystemApi Allows an application to control the backup and restore process.
|
||||
<p>Not for use by third-party applications.
|
||||
@hide pending API council -->
|
||||
@@ -3859,6 +3865,16 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name="com.android.server.stats.StatsCompanionService$AnomalyAlarmReceiver"
|
||||
android:permission="android.permission.STATSCOMPANION"
|
||||
android:exported="false">
|
||||
</receiver>
|
||||
|
||||
<receiver android:name="com.android.server.stats.StatsCompanionService$PollingAlarmReceiver"
|
||||
android:permission="android.permission.STATSCOMPANION"
|
||||
android:exported="false">
|
||||
</receiver>
|
||||
|
||||
<service android:name="android.hardware.location.GeofenceHardwareService"
|
||||
android:permission="android.permission.LOCATION_HARDWARE"
|
||||
android:exported="false" />
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package com.android.server.stats;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.IStatsCompanionService;
|
||||
import android.os.IStatsManager;
|
||||
import android.os.Process;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.SystemService;
|
||||
|
||||
/**
|
||||
* Helper service for statsd (the native stats management service in cmds/statsd/).
|
||||
* Used for registering and receiving alarms on behalf of statsd.
|
||||
*/
|
||||
public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
static final String TAG = "StatsCompanionService";
|
||||
static final boolean DEBUG = true;
|
||||
|
||||
private final Context mContext;
|
||||
private final AlarmManager mAlarmManager;
|
||||
private final IStatsManager mStatsd;
|
||||
|
||||
private final PendingIntent mAnomalyAlarmIntent;
|
||||
private final PendingIntent mPollingAlarmIntent;
|
||||
|
||||
public final static class AnomalyAlarmReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred.");
|
||||
// TODO: mStatsd.informAlarm(); // should be twoway so device won't sleep before acting?
|
||||
// AlarmManager releases its own wakelock here.
|
||||
}
|
||||
};
|
||||
|
||||
public final static class PollingAlarmReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (DEBUG) Slog.d(TAG, "Time to poll something.");
|
||||
// TODO: mStatsd.poll(); // should be twoway so device won't sleep before acting?
|
||||
// AlarmManager releases its own wakelock here.
|
||||
}
|
||||
};
|
||||
|
||||
public StatsCompanionService(Context context) {
|
||||
super();
|
||||
mContext = context;
|
||||
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
|
||||
|
||||
mAnomalyAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
|
||||
new Intent(mContext, AnomalyAlarmReceiver.class), 0);
|
||||
mPollingAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
|
||||
new Intent(mContext, PollingAlarmReceiver.class), 0);
|
||||
|
||||
mStatsd = getStatsdService();
|
||||
}
|
||||
|
||||
/** Returns the statsd IBinder service */
|
||||
public static IStatsManager getStatsdService() {
|
||||
return IStatsManager.Stub.asInterface(ServiceManager.getService("statsd"));
|
||||
}
|
||||
|
||||
public static final class Lifecycle extends SystemService {
|
||||
private StatsCompanionService mStatsCompanionService;
|
||||
|
||||
public Lifecycle(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
mStatsCompanionService = new StatsCompanionService(getContext());
|
||||
try {
|
||||
publishBinderService(Context.STATS_COMPANION_SERVICE, mStatsCompanionService);
|
||||
if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE);
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Failed to publishBinderService", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void setAnomalyAlarm(long timestampMs) {
|
||||
enforceCallingPermission();
|
||||
if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs);
|
||||
final long callingToken = Binder.clearCallingIdentity();
|
||||
try {
|
||||
// using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
|
||||
// This alarm is inexact, leaving its exactness completely up to the OS optimizations.
|
||||
// AlarmManager will automatically cancel any previous mAnomalyAlarmIntent alarm.
|
||||
mAlarmManager.set(AlarmManager.RTC, timestampMs, mAnomalyAlarmIntent);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(callingToken);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void cancelAnomalyAlarm() {
|
||||
enforceCallingPermission();
|
||||
if (DEBUG) Slog.d(TAG, "Cancelling anomaly alarm");
|
||||
final long callingToken = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mAlarmManager.cancel(mAnomalyAlarmIntent);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(callingToken);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void setPollingAlarms(long timestampMs, long intervalMs) {
|
||||
enforceCallingPermission();
|
||||
if (DEBUG) Slog.d(TAG, "Setting polling alarm for " + timestampMs
|
||||
+ " every " + intervalMs + "ms");
|
||||
final long callingToken = Binder.clearCallingIdentity();
|
||||
try {
|
||||
// using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
|
||||
// This alarm is inexact, leaving its exactness completely up to the OS optimizations.
|
||||
// TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
|
||||
mAlarmManager.setRepeating(AlarmManager.RTC, timestampMs, intervalMs,
|
||||
mPollingAlarmIntent);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(callingToken);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void cancelPollingAlarms() {
|
||||
enforceCallingPermission();
|
||||
if (DEBUG) Slog.d(TAG, "Cancelling polling alarm");
|
||||
final long callingToken = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mAlarmManager.cancel(mPollingAlarmIntent);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(callingToken);
|
||||
}
|
||||
}
|
||||
|
||||
private void enforceCallingPermission() {
|
||||
if (Binder.getCallingPid() == Process.myPid()) {
|
||||
return;
|
||||
}
|
||||
mContext.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -103,6 +103,7 @@ import com.android.server.restrictions.RestrictionsManagerService;
|
||||
import com.android.server.security.KeyAttestationApplicationIdProviderService;
|
||||
import com.android.server.security.KeyChainSystemService;
|
||||
import com.android.server.soundtrigger.SoundTriggerService;
|
||||
import com.android.server.stats.StatsCompanionService;
|
||||
import com.android.server.statusbar.StatusBarManagerService;
|
||||
import com.android.server.storage.DeviceStorageMonitorService;
|
||||
import com.android.server.telecom.TelecomLoaderService;
|
||||
@@ -1523,6 +1524,11 @@ public final class SystemServer {
|
||||
traceEnd();
|
||||
}
|
||||
|
||||
// Statsd helper
|
||||
traceBeginAndSlog("StartStatsCompanionService");
|
||||
mSystemServiceManager.startService(StatsCompanionService.Lifecycle.class);
|
||||
traceEnd();
|
||||
|
||||
// Before things start rolling, be sure we have decided whether
|
||||
// we are in safe mode.
|
||||
final boolean safeMode = wm.detectSafeMode();
|
||||
|
||||
Reference in New Issue
Block a user