blob: ff1cb4ff1450559c0e917d1f614ad83a676d3b2d [file] [log] [blame]
/*
* Copyright (C) 2018 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 DEBUG false
#include "Log.h"
#include "FieldValue.h"
#include "IncidentdReporter.h"
#include "packages/UidMap.h"
#include "stats_log_util.h"
#include <android/os/IIncidentManager.h>
#include <android/os/IncidentReportArgs.h>
#include <android/util/ProtoOutputStream.h>
#include <binder/IBinder.h>
#include <binder/IServiceManager.h>
#include <vector>
namespace android {
namespace os {
namespace statsd {
using android::util::ProtoOutputStream;
using std::vector;
using util::FIELD_TYPE_MESSAGE;
using util::FIELD_TYPE_INT32;
using util::FIELD_TYPE_INT64;
// field ids in IncidentHeaderProto
const int FIELD_ID_ALERT_ID = 1;
const int FIELD_ID_CONFIG_KEY = 3;
const int FIELD_ID_CONFIG_KEY_UID = 1;
const int FIELD_ID_CONFIG_KEY_ID = 2;
const int FIELD_ID_TRIGGER_DETAILS = 4;
const int FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC = 1;
const int FIELD_ID_METRIC_VALUE_METRIC_ID = 1;
const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT = 2;
const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION = 3;
const int FIELD_ID_METRIC_VALUE_VALUE = 4;
const int FIELD_ID_PACKAGE_INFO = 3;
namespace {
void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensionKey& dimensionKey,
int64_t metricValue, const ConfigKey& configKey, vector<uint8_t>* protoData) {
ProtoOutputStream headerProto;
headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_ID, (long long)rule_id);
uint64_t token =
headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
headerProto.write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_KEY_UID, configKey.GetUid());
headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_KEY_ID, (long long)configKey.GetId());
headerProto.end(token);
token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS);
// MetricValue trigger_metric = 1;
uint64_t metricToken =
headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC);
// message MetricValue {
// optional int64 metric_id = 1;
headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_METRIC_ID, (long long)metricId);
// optional DimensionsValue dimension_in_what = 2;
uint64_t dimToken =
headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), nullptr, &headerProto);
headerProto.end(dimToken);
// optional DimensionsValue dimension_in_condition = 3;
dimToken = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION);
writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), nullptr, &headerProto);
headerProto.end(dimToken);
// optional int64 value = 4;
headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_VALUE, (long long)metricValue);
// }
headerProto.end(metricToken);
// write relevant uid package info
std::set<int32_t> uids;
for (const auto& dim : dimensionKey.getDimensionKeyInWhat().getValues()) {
int uid = getUidIfExists(dim);
// any uid <= 2000 are predefined AID_*
if (uid > 2000) {
uids.insert(uid);
}
}
for (const auto& dim : dimensionKey.getDimensionKeyInCondition().getValues()) {
int uid = getUidIfExists(dim);
if (uid > 2000) {
uids.insert(uid);
}
}
if (!uids.empty()) {
uint64_t token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_PACKAGE_INFO);
UidMap::getInstance()->writeUidMapSnapshot(getElapsedRealtimeNs(), true, true, uids,
nullptr /*string set*/, &headerProto);
headerProto.end(token);
}
headerProto.end(token);
protoData->resize(headerProto.size());
size_t pos = 0;
sp<android::util::ProtoReader> reader = headerProto.data();
while (reader->readBuffer() != NULL) {
size_t toRead = reader->currentToRead();
std::memcpy(&((*protoData)[pos]), reader->readBuffer(), toRead);
pos += toRead;
reader->move(toRead);
}
}
} // namespace
bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId,
const MetricDimensionKey& dimensionKey, int64_t metricValue,
const ConfigKey& configKey) {
if (config.section_size() == 0) {
VLOG("The alert %lld contains zero section in config(%d,%lld)", (unsigned long long)rule_id,
configKey.GetUid(), (long long)configKey.GetId());
return false;
}
IncidentReportArgs incidentReport;
vector<uint8_t> protoData;
getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey, &protoData);
incidentReport.addHeader(protoData);
for (int i = 0; i < config.section_size(); i++) {
incidentReport.addSection(config.section(i));
}
uint8_t dest;
switch (config.dest()) {
case IncidentdDetails_Destination_AUTOMATIC:
dest = android::os::PRIVACY_POLICY_AUTOMATIC;
break;
case IncidentdDetails_Destination_EXPLICIT:
dest = android::os::PRIVACY_POLICY_EXPLICIT;
break;
default:
dest = android::os::PRIVACY_POLICY_AUTOMATIC;
}
incidentReport.setPrivacyPolicy(dest);
incidentReport.setReceiverPkg(config.receiver_pkg());
incidentReport.setReceiverCls(config.receiver_cls());
sp<IIncidentManager> service = interface_cast<IIncidentManager>(
defaultServiceManager()->getService(android::String16("incident")));
if (service == nullptr) {
ALOGW("Failed to fetch incident service.");
return false;
}
VLOG("Calling incidentd %p", service.get());
binder::Status s = service->reportIncident(incidentReport);
VLOG("Report incident status: %s", s.toString8().string());
return s.isOk();
}
} // namespace statsd
} // namespace os
} // namespace android