blob: 6949df7419d60cccff4e5284bc78ec89255d34f7 [file] [log] [blame]
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#if USE_SYSLOG
#include <syslog.h>
#endif
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "extern.h"
#include "watch_err.h"
#include <cutils/properties.h>
#include <flash_ts.h>
#define min(a,b) (((a) < (b)) ? (a) : (b))
// Table of boolean system properties which will be synced with fts.
static const char *kKeyProperty[] = {
// property recording first activation date.
"persist.chrome.activation_date",
#if !defined(REMOVE_UMA_CONTENT_FROM_FTS)
// property recording device UMA client id, not linked with serial number.
"persist.chrome.client_id",
#endif // #if !defined(REMOVE_UMA_CONTENT_FROM_FTS)
// property recording total number of FDR events to date.
"persist.chrome.fdr_count",
// property recording total number of FDR events to date, but incremented
// in fts.
"persist.fdr_watchdog",
#if !defined(REMOVE_UMA_CONTENT_FROM_FTS)
// property recording user opt-out of sending usage report. Keep the name
// as "opt_in" for backward-compatibility, see "usage_report" below.
"persist.chrome.opt_in.stats",
#endif // #if !defined(REMOVE_UMA_CONTENT_FROM_FTS)
// property recording whether omaha checkin has occurred.
"persist.omaha.checkin",
// property for device color set in factory.
"persist.color",
// property for country code set in factory.
"ro.country",
// property for language tag set in factory.
"ro.lang",
// property set to 'true' if supports low vcore. 'false' otherwise.
"ro.low_vcore_enabled",
};
// Matching fts keys for above properties.
static const char *kKeyFTS[] = {
"activation_date",
#if !defined(REMOVE_UMA_CONTENT_FROM_FTS)
"client_id",
#endif // #if !defined(REMOVE_UMA_CONTENT_FROM_FTS)
"fdr_count",
"fdr_watchdog",
#if !defined(REMOVE_UMA_CONTENT_FROM_FTS)
// usage_report should be interpreted as opt-out, i.e.
// usage_report=0 means user opt out of sending usage_report;
// usage_report=1 means user did not opt out.
"usage_report",
#endif // #if !defined(REMOVE_UMA_CONTENT_FROM_FTS)
"omaha_checkin",
"color",
"ro.country",
"ro.lang",
"ro.low_vcore_enabled",
};
static int open_fts() {
int fd = open(FTS_DEVICE, O_RDWR);
if (fd == -1) {
// Only print out the warning once to avoid flooding log.
static int warned = 0;
if (warned) return -1;
warned = 1;
#if USE_SYSLOG
syslog(LOG_ERR,
#else
fprintf(stderr,
#endif
"error open %s: %s\n", FTS_DEVICE, strerror(errno));
}
return fd;
}
// Helper to read FTS property.
static int read_fts_property(const int fd, struct flash_ts_io_req *req,
const char *key) {
if (!req || !key || fd < 0) return -1;
memset(req, 0, sizeof(*req));
snprintf(req->key, sizeof(req->key), "%s", key);
if (ioctl(fd, FLASH_TS_IO_GET, req)) {
#if USE_SYSLOG
syslog(LOG_ERR,
#else
fprintf(stderr,
#endif
"ioctl FLASH_TS_IO_GET fail %s\n", strerror(errno));
return -1;
}
return 0;
}
static int write_fts_property(const int fd, const char *key, const char *value) {
if (!key || !value || fd < 0) return -1;
struct flash_ts_io_req req;
memset(&req, 0, sizeof(req));
snprintf(req.key, sizeof(req.key), "%s", key);
snprintf(req.val, sizeof(req.val), "%s", value);
if (ioctl(fd, FLASH_TS_IO_SET, &req)) {
#if USE_SYSLOG
syslog(LOG_ERR,
#else
fprintf(stderr,
#endif
"ioctl FLASH_TS_IO_SET fail %s\n", strerror(errno));
return -1;
}
return 0;
}
// Sync information between system properties and fts.
//
// The fts_to_prop parameter indicates the direction.
//
// We sync from fts to properties only at boot.
//
// We sync from properties to fts periodically so that
// the properties become persistent across FDRs.
//
// Some properties (e.g. ro.*) are only sync'ed one-way.
static void sync_properties_fts(int fts_to_prop) {
// open fts
int fd = open_fts();
if (fd < 0) return;
for (size_t index = 0; index < sizeof(kKeyProperty) / sizeof(*kKeyProperty);
++index) {
int ro_prop = (strncmp("ro.", kKeyProperty[index], 3) == 0);
// Only copy read-only fts values to properties, not the other way.
// This means that these properties can only be set by changing the
// FTS.
//
// Note: we could change this behavior if we wanted this
// functionality.
if ((ro_prop || !strcmp(kKeyFTS[index], "fdr_watchdog")) &&
!fts_to_prop) {
continue;
}
// read fts
struct flash_ts_io_req req;
if (read_fts_property(fd, &req, kKeyFTS[index]) == -1) {
close(fd);
return;
}
// read property
char value[PROPERTY_VALUE_MAX];
property_get(kKeyProperty[index], value, "");
// If values are identical, there is nothing to do.
if (strncmp(value, req.val, min(sizeof(req.val), PROPERTY_VALUE_MAX)) ==
0) {
continue;
}
// Sync them based on direction and values.
// We always want to overwrite the FDR property.
if (fts_to_prop &&
(*value == '\0' || !strcmp(kKeyFTS[index], "fdr_watchdog"))) {
// We are running the one-time initialization script.
//
// If a property is unset (and therefore the fts must be set)
// then initialize the property to the fts value.
//
// If a property is set (and our fts hasn't been set yet),
// then do nothing. The next sync after booting will remedy
// this
// discrepancy. This could happen if we reboot after
// the property is set, but before the fts is synced.
property_set(kKeyProperty[index], req.val);
} else if (!fts_to_prop && *value != '\0') {
// We are running the periodic sync process.
//
// If a property is set (and the fts value is different
// or unset), set fts to the current property value.
//
// Note: we don't support clearing the fts
// (accidentally or intentionally) as these properties
// are intended to be sticky. For example, removing
// /data/properties/* or doing a setprop with a null
// value won't change fts. This doesn't normally occur
// and the value will be reset at next boot.
if (write_fts_property(fd, kKeyFTS[index], value) == -1) {
close(fd);
return;
}
}
}
close(fd);
}
// helper functions which sync in each direction.
void copy_fts_to_properties() {
sync_properties_fts(1);
}
void copy_properties_to_fts() {
sync_properties_fts(0);
}
void increment_fdr_count() {
// Only increment once maximum per session, to avoid edge cases such as the
// app + button causing fdr at the same time and the count incrementing
// twice.
static int fdr_incremented = 0;
if (fdr_incremented) return;
++fdr_incremented;
int fd = open_fts();
if (fd < 0) {
return;
}
struct flash_ts_io_req req;
if (read_fts_property(fd, &req, "fdr_watchdog") == -1) {
close(fd);
return;
}
int fdr_count = strtol(req.val, NULL, 10);
char fdr_count_str[FLASH_TS_MAX_VAL_SIZE];
sprintf(fdr_count_str, "%d", fdr_count + 1);
write_fts_property(fd, req.key, fdr_count_str);
close(fd);
}