Merge changes I82fe220e,Iff324c73,I3bd73acd

* changes:
  indexed priority queue for AnomalyMonitor
  StatsCompanionService connection to Statsd
  Set up StatsCompanionService.java
This commit is contained in:
Adam Bookatz
2017-09-20 14:01:49 +00:00
committed by Android (Google) Code Review
11 changed files with 865 additions and 0 deletions

View File

@@ -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 \

View File

@@ -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 \

View 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

View 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

View 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

View 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

View File

@@ -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.

View 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();
}

View File

@@ -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" />

View File

@@ -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);
}
}

View File

@@ -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();