1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define STATSD_DEBUG false // STOPSHIP if true
18
19 #include "Log.h"
20
21 #include "utils/DbUtils.h"
22
23 #include <android/api-level.h>
24
25 #include "FieldValue.h"
26 #include "android-base/properties.h"
27 #include "android-base/stringprintf.h"
28 #include "stats_log_util.h"
29 #include "storage/StorageManager.h"
30
31 namespace android {
32 namespace os {
33 namespace statsd {
34 namespace dbutils {
35
36 using ::android::os::statsd::FLOAT;
37 using ::android::os::statsd::INT;
38 using ::android::os::statsd::LONG;
39 using ::android::os::statsd::StorageManager;
40 using ::android::os::statsd::STRING;
41 using base::GetProperty;
42 using base::StringPrintf;
43
44 const string TABLE_NAME_PREFIX = "metric_";
45 const string COLUMN_NAME_ATOM_TAG = "atomId";
46 const string COLUMN_NAME_EVENT_ELAPSED_CLOCK_NS = "elapsedTimestampNs";
47 const string COLUMN_NAME_EVENT_WALL_CLOCK_NS = "wallTimestampNs";
48
49 const string COLUMN_NAME_SDK_VERSION = "sdkVersion";
50 const string COLUMN_NAME_MODEL = "model";
51 const string COLUMN_NAME_PRODUCT = "product";
52 const string COLUMN_NAME_HARDWARE = "hardware";
53 const string COLUMN_NAME_DEVICE = "device";
54 const string COLUMN_NAME_BUILD = "osBuild";
55 const string COLUMN_NAME_FINGERPRINT = "fingerprint";
56 const string COLUMN_NAME_BRAND = "brand";
57 const string COLUMN_NAME_MANUFACTURER = "manufacturer";
58 const string COLUMN_NAME_BOARD = "board";
59
getExpectedTableSchema(const LogEvent & logEvent)60 static std::vector<std::string> getExpectedTableSchema(const LogEvent& logEvent) {
61 vector<std::string> result;
62 for (const FieldValue& fieldValue : logEvent.getValues()) {
63 if (fieldValue.mField.getDepth() > 0) {
64 // Repeated fields are not supported.
65 continue;
66 }
67 switch (fieldValue.mValue.getType()) {
68 case INT:
69 case LONG:
70 result.push_back("INTEGER");
71 break;
72 case STRING:
73 result.push_back("TEXT");
74 break;
75 case FLOAT:
76 result.push_back("REAL");
77 break;
78 default:
79 // Byte array fields are not supported.
80 break;
81 }
82 }
83 return result;
84 }
85
integrityCheckCallback(void *,int colCount,char ** queryResults,char **)86 static int integrityCheckCallback(void*, int colCount, char** queryResults, char**) {
87 if (colCount == 0 || strcmp(queryResults[0], "ok") != 0) {
88 // Returning 1 is an error code that causes exec to stop and error.
89 return 1;
90 }
91 return 0;
92 }
93
getDbName(const ConfigKey & key)94 string getDbName(const ConfigKey& key) {
95 return StringPrintf("%s/%d_%lld.db", STATS_RESTRICTED_DATA_DIR, key.GetUid(),
96 (long long)key.GetId());
97 }
98
getCreateSqlString(const int64_t metricId,const LogEvent & event)99 static string getCreateSqlString(const int64_t metricId, const LogEvent& event) {
100 string result = StringPrintf("CREATE TABLE IF NOT EXISTS %s%s", TABLE_NAME_PREFIX.c_str(),
101 reformatMetricId(metricId).c_str());
102 result += StringPrintf("(%s INTEGER,%s INTEGER,%s INTEGER,", COLUMN_NAME_ATOM_TAG.c_str(),
103 COLUMN_NAME_EVENT_ELAPSED_CLOCK_NS.c_str(),
104 COLUMN_NAME_EVENT_WALL_CLOCK_NS.c_str());
105 for (size_t fieldId = 1; fieldId <= event.getValues().size(); ++fieldId) {
106 const FieldValue& fieldValue = event.getValues()[fieldId - 1];
107 if (fieldValue.mField.getDepth() > 0) {
108 // Repeated fields are not supported.
109 continue;
110 }
111 switch (fieldValue.mValue.getType()) {
112 case INT:
113 case LONG:
114 result += StringPrintf("field_%d INTEGER,", fieldValue.mField.getPosAtDepth(0));
115 break;
116 case STRING:
117 result += StringPrintf("field_%d TEXT,", fieldValue.mField.getPosAtDepth(0));
118 break;
119 case FLOAT:
120 result += StringPrintf("field_%d REAL,", fieldValue.mField.getPosAtDepth(0));
121 break;
122 default:
123 // Byte array fields are not supported.
124 break;
125 }
126 }
127 result.pop_back();
128 result += ") STRICT;";
129 return result;
130 }
131
reformatMetricId(const int64_t metricId)132 string reformatMetricId(const int64_t metricId) {
133 return metricId < 0 ? StringPrintf("n%lld", (long long)metricId * -1)
134 : StringPrintf("%lld", (long long)metricId);
135 }
136
createTableIfNeeded(const ConfigKey & key,const int64_t metricId,const LogEvent & event)137 bool createTableIfNeeded(const ConfigKey& key, const int64_t metricId, const LogEvent& event) {
138 const string dbName = getDbName(key);
139 sqlite3* db;
140 if (sqlite3_open(dbName.c_str(), &db) != SQLITE_OK) {
141 sqlite3_close(db);
142 return false;
143 }
144
145 char* error = nullptr;
146 string zSql = getCreateSqlString(metricId, event);
147 sqlite3_exec(db, zSql.c_str(), nullptr, nullptr, &error);
148 sqlite3_close(db);
149 if (error) {
150 ALOGW("Failed to create table to db: %s", error);
151 return false;
152 }
153 return true;
154 }
155
isEventCompatible(const ConfigKey & key,const int64_t metricId,const LogEvent & event)156 bool isEventCompatible(const ConfigKey& key, const int64_t metricId, const LogEvent& event) {
157 const string dbName = getDbName(key);
158 sqlite3* db;
159 if (sqlite3_open(dbName.c_str(), &db) != SQLITE_OK) {
160 sqlite3_close(db);
161 return false;
162 }
163 string zSql = StringPrintf("PRAGMA table_info(metric_%s);", reformatMetricId(metricId).c_str());
164 string err;
165 std::vector<int32_t> columnTypes;
166 std::vector<string> columnNames;
167 std::vector<std::vector<std::string>> rows;
168 if (!query(key, zSql, rows, columnTypes, columnNames, err)) {
169 ALOGE("Failed to check table schema for metric %lld: %s", (long long)metricId, err.c_str());
170 sqlite3_close(db);
171 return false;
172 }
173 // Sample query result
174 // cid name type notnull dflt_value pk
175 // --- ----------------- ------- ------- ---------- --
176 // 0 atomId INTEGER 0 (null) 0
177 // 1 elapsedTimestampNs INTEGER 0 (null) 0
178 // 2 wallTimestampNs INTEGER 0 (null) 0
179 // 3 field_1 INTEGER 0 (null) 0
180 // 4 field_2 TEXT 0 (null) 0
181 std::vector<string> tableSchema;
182 for (size_t i = 3; i < rows.size(); ++i) { // Atom fields start at the third row
183 tableSchema.push_back(rows[i][2]); // The third column stores the data type for the column
184 }
185 sqlite3_close(db);
186 // An empty rows vector implies the table has not yet been created.
187 return rows.size() == 0 || getExpectedTableSchema(event) == tableSchema;
188 }
189
deleteTable(const ConfigKey & key,const int64_t metricId)190 bool deleteTable(const ConfigKey& key, const int64_t metricId) {
191 const string dbName = getDbName(key);
192 sqlite3* db;
193 if (sqlite3_open(dbName.c_str(), &db) != SQLITE_OK) {
194 sqlite3_close(db);
195 return false;
196 }
197 string zSql = StringPrintf("DROP TABLE metric_%s", reformatMetricId(metricId).c_str());
198 char* error = nullptr;
199 sqlite3_exec(db, zSql.c_str(), nullptr, nullptr, &error);
200 sqlite3_close(db);
201 if (error) {
202 ALOGW("Failed to drop table from db: %s", error);
203 return false;
204 }
205 return true;
206 }
207
deleteDb(const ConfigKey & key)208 void deleteDb(const ConfigKey& key) {
209 const string dbName = getDbName(key);
210 StorageManager::deleteFile(dbName.c_str());
211 }
212
getDb(const ConfigKey & key)213 sqlite3* getDb(const ConfigKey& key) {
214 const string dbName = getDbName(key);
215 sqlite3* db;
216 if (sqlite3_open(dbName.c_str(), &db) == SQLITE_OK) {
217 return db;
218 }
219 return nullptr;
220 }
221
closeDb(sqlite3 * db)222 void closeDb(sqlite3* db) {
223 sqlite3_close(db);
224 }
225
getInsertSqlStmt(sqlite3 * db,sqlite3_stmt ** stmt,const int64_t metricId,const vector<LogEvent> & events,string & err)226 static bool getInsertSqlStmt(sqlite3* db, sqlite3_stmt** stmt, const int64_t metricId,
227 const vector<LogEvent>& events, string& err) {
228 string result =
229 StringPrintf("INSERT INTO metric_%s VALUES", reformatMetricId(metricId).c_str());
230 for (auto& logEvent : events) {
231 result += StringPrintf("(%d, %lld, %lld,", logEvent.GetTagId(),
232 (long long)logEvent.GetElapsedTimestampNs(),
233 (long long)logEvent.GetLogdTimestampNs());
234 for (auto& fieldValue : logEvent.getValues()) {
235 if (fieldValue.mField.getDepth() > 0 || fieldValue.mValue.getType() == STORAGE) {
236 // Repeated fields and byte fields are not supported.
237 continue;
238 }
239 result += "?,";
240 }
241 result.pop_back();
242 result += "),";
243 }
244 result.pop_back();
245 result += ";";
246 if (sqlite3_prepare_v2(db, result.c_str(), -1, stmt, nullptr) != SQLITE_OK) {
247 err = sqlite3_errmsg(db);
248 return false;
249 }
250 // ? parameters start with an index of 1 from start of query string to the
251 // end.
252 int32_t index = 1;
253 for (auto& logEvent : events) {
254 for (auto& fieldValue : logEvent.getValues()) {
255 if (fieldValue.mField.getDepth() > 0 || fieldValue.mValue.getType() == STORAGE) {
256 // Repeated fields and byte fields are not supported.
257 continue;
258 }
259 switch (fieldValue.mValue.getType()) {
260 case INT:
261 sqlite3_bind_int(*stmt, index, fieldValue.mValue.int_value);
262 break;
263 case LONG:
264 sqlite3_bind_int64(*stmt, index, fieldValue.mValue.long_value);
265 break;
266 case STRING:
267 sqlite3_bind_text(*stmt, index, fieldValue.mValue.str_value.c_str(), -1,
268 SQLITE_STATIC);
269 break;
270 case FLOAT:
271 sqlite3_bind_double(*stmt, index, fieldValue.mValue.float_value);
272 break;
273 default:
274 // Byte array fields are not supported.
275 break;
276 }
277 ++index;
278 }
279 }
280 return true;
281 }
282
insert(const ConfigKey & key,const int64_t metricId,const vector<LogEvent> & events,string & error)283 bool insert(const ConfigKey& key, const int64_t metricId, const vector<LogEvent>& events,
284 string& error) {
285 const string dbName = getDbName(key);
286 sqlite3* db;
287 if (sqlite3_open(dbName.c_str(), &db) != SQLITE_OK) {
288 error = sqlite3_errmsg(db);
289 sqlite3_close(db);
290 return false;
291 }
292 bool success = insert(db, metricId, events, error);
293 sqlite3_close(db);
294 return success;
295 }
296
insert(sqlite3 * db,const int64_t metricId,const vector<LogEvent> & events,string & error)297 bool insert(sqlite3* db, const int64_t metricId, const vector<LogEvent>& events, string& error) {
298 sqlite3_stmt* stmt = nullptr;
299 if (!getInsertSqlStmt(db, &stmt, metricId, events, error)) {
300 ALOGW("Failed to generate prepared sql insert query %s", error.c_str());
301 sqlite3_finalize(stmt);
302 return false;
303 }
304 if (sqlite3_step(stmt) != SQLITE_DONE) {
305 error = sqlite3_errmsg(db);
306 ALOGW("Failed to insert data to db: %s", error.c_str());
307 sqlite3_finalize(stmt);
308 return false;
309 }
310 sqlite3_finalize(stmt);
311 return true;
312 }
313
query(const ConfigKey & key,const string & zSql,vector<vector<string>> & rows,vector<int32_t> & columnTypes,vector<string> & columnNames,string & err)314 bool query(const ConfigKey& key, const string& zSql, vector<vector<string>>& rows,
315 vector<int32_t>& columnTypes, vector<string>& columnNames, string& err) {
316 const string dbName = getDbName(key);
317 sqlite3* db;
318 if (sqlite3_open_v2(dbName.c_str(), &db, SQLITE_OPEN_READONLY, nullptr) != SQLITE_OK) {
319 err = sqlite3_errmsg(db);
320 sqlite3_close(db);
321 return false;
322 }
323 sqlite3_stmt* stmt;
324 if (sqlite3_prepare_v2(db, zSql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
325 err = sqlite3_errmsg(db);
326 sqlite3_finalize(stmt);
327 sqlite3_close(db);
328 return false;
329 }
330 int result = sqlite3_step(stmt);
331 bool firstIter = true;
332 while (result == SQLITE_ROW) {
333 int colCount = sqlite3_column_count(stmt);
334 vector<string> rowData(colCount);
335 for (int i = 0; i < colCount; ++i) {
336 if (firstIter) {
337 int32_t columnType = sqlite3_column_type(stmt, i);
338 // Needed to convert to java compatible cursor types. See AbstractCursor#getType()
339 if (columnType == 5) {
340 columnType = 0; // Remap 5 (null type) to 0 for java cursor
341 }
342 columnTypes.push_back(columnType);
343 columnNames.push_back(reinterpret_cast<const char*>(sqlite3_column_name(stmt, i)));
344 }
345 const unsigned char* textResult = sqlite3_column_text(stmt, i);
346 string colData =
347 textResult != nullptr ? string(reinterpret_cast<const char*>(textResult)) : "";
348 rowData[i] = std::move(colData);
349 }
350 rows.push_back(std::move(rowData));
351 firstIter = false;
352 result = sqlite3_step(stmt);
353 }
354 sqlite3_finalize(stmt);
355 if (result != SQLITE_DONE) {
356 err = sqlite3_errmsg(db);
357 sqlite3_close(db);
358 return false;
359 }
360 sqlite3_close(db);
361 return true;
362 }
363
flushTtl(sqlite3 * db,const int64_t metricId,const int64_t ttlWallClockNs)364 bool flushTtl(sqlite3* db, const int64_t metricId, const int64_t ttlWallClockNs) {
365 string zSql = StringPrintf("DELETE FROM %s%s WHERE %s <= %lld", TABLE_NAME_PREFIX.c_str(),
366 reformatMetricId(metricId).c_str(),
367 COLUMN_NAME_EVENT_WALL_CLOCK_NS.c_str(), (long long)ttlWallClockNs);
368
369 char* error = nullptr;
370 sqlite3_exec(db, zSql.c_str(), nullptr, nullptr, &error);
371 if (error) {
372 ALOGW("Failed to enforce ttl: %s", error);
373 return false;
374 }
375 return true;
376 }
377
verifyIntegrityAndDeleteIfNecessary(const ConfigKey & configKey)378 void verifyIntegrityAndDeleteIfNecessary(const ConfigKey& configKey) {
379 const string dbName = getDbName(configKey);
380 sqlite3* db;
381 if (sqlite3_open(dbName.c_str(), &db) != SQLITE_OK) {
382 sqlite3_close(db);
383 return;
384 }
385 string zSql = "PRAGMA integrity_check";
386
387 char* error = nullptr;
388 sqlite3_exec(db, zSql.c_str(), integrityCheckCallback, nullptr, &error);
389 if (error) {
390 StatsdStats::getInstance().noteDbCorrupted(configKey);
391 ALOGW("Integrity Check failed %s", error);
392 sqlite3_close(db);
393 deleteDb(configKey);
394 return;
395 }
396 sqlite3_close(db);
397 }
398
getDeviceInfoInsertStmt(sqlite3 * db,sqlite3_stmt ** stmt,string error)399 static bool getDeviceInfoInsertStmt(sqlite3* db, sqlite3_stmt** stmt, string error) {
400 string insertSql = StringPrintf("INSERT INTO device_info VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
401 if (sqlite3_prepare_v2(db, insertSql.c_str(), -1, stmt, nullptr) != SQLITE_OK) {
402 error = sqlite3_errmsg(db);
403 return false;
404 }
405
406 // ? parameters start with an index of 1 from start of query string to the end.
407 int32_t index = 1;
408
409 int32_t sdkVersion = android_get_device_api_level();
410 sqlite3_bind_int(*stmt, index, sdkVersion);
411 ++index;
412
413 string model = GetProperty("ro.product.model", "(unknown)");
414 sqlite3_bind_text(*stmt, index, model.c_str(), -1, SQLITE_TRANSIENT);
415 ++index;
416
417 string product = GetProperty("ro.product.name", "(unknown)");
418 sqlite3_bind_text(*stmt, index, product.c_str(), -1, SQLITE_TRANSIENT);
419 ++index;
420
421 string hardware = GetProperty("ro.hardware", "(unknown)");
422 sqlite3_bind_text(*stmt, index, hardware.c_str(), -1, SQLITE_TRANSIENT);
423 ++index;
424
425 string device = GetProperty("ro.product.device", "(unknown)");
426 sqlite3_bind_text(*stmt, index, device.c_str(), -1, SQLITE_TRANSIENT);
427 ++index;
428
429 string osBuild = GetProperty("ro.build.id", "(unknown)");
430 sqlite3_bind_text(*stmt, index, osBuild.c_str(), -1, SQLITE_TRANSIENT);
431 ++index;
432
433 string fingerprint = GetProperty("ro.build.fingerprint", "(unknown)");
434 sqlite3_bind_text(*stmt, index, fingerprint.c_str(), -1, SQLITE_TRANSIENT);
435 ++index;
436
437 string brand = GetProperty("ro.product.brand", "(unknown)");
438 sqlite3_bind_text(*stmt, index, brand.c_str(), -1, SQLITE_TRANSIENT);
439 ++index;
440
441 string manufacturer = GetProperty("ro.product.manufacturer", "(unknown)");
442 sqlite3_bind_text(*stmt, index, manufacturer.c_str(), -1, SQLITE_TRANSIENT);
443 ++index;
444
445 string board = GetProperty("ro.product.board", "(unknown)");
446 sqlite3_bind_text(*stmt, index, board.c_str(), -1, SQLITE_TRANSIENT);
447 ++index;
448
449 return true;
450 }
451
updateDeviceInfoTable(const ConfigKey & key,string & error)452 bool updateDeviceInfoTable(const ConfigKey& key, string& error) {
453 const string dbName = getDbName(key);
454 sqlite3* db;
455 if (sqlite3_open(dbName.c_str(), &db) != SQLITE_OK) {
456 error = sqlite3_errmsg(db);
457 sqlite3_close(db);
458 return false;
459 }
460
461 string dropTableSql = "DROP TABLE device_info";
462 // Ignore possible error result code if table has not yet been created.
463 sqlite3_exec(db, dropTableSql.c_str(), nullptr, nullptr, nullptr);
464
465 string createTableSql = StringPrintf(
466 "CREATE TABLE device_info(%s INTEGER, %s TEXT, %s TEXT, %s TEXT, %s TEXT, %s TEXT, %s "
467 "TEXT, %s TEXT, %s TEXT, %s TEXT) "
468 "STRICT",
469 COLUMN_NAME_SDK_VERSION.c_str(), COLUMN_NAME_MODEL.c_str(), COLUMN_NAME_PRODUCT.c_str(),
470 COLUMN_NAME_HARDWARE.c_str(), COLUMN_NAME_DEVICE.c_str(), COLUMN_NAME_BUILD.c_str(),
471 COLUMN_NAME_FINGERPRINT.c_str(), COLUMN_NAME_BRAND.c_str(),
472 COLUMN_NAME_MANUFACTURER.c_str(), COLUMN_NAME_BOARD.c_str());
473 if (sqlite3_exec(db, createTableSql.c_str(), nullptr, nullptr, nullptr) != SQLITE_OK) {
474 error = sqlite3_errmsg(db);
475 ALOGW("Failed to create device info table %s", error.c_str());
476 sqlite3_close(db);
477 return false;
478 }
479
480 sqlite3_stmt* stmt = nullptr;
481 if (!getDeviceInfoInsertStmt(db, &stmt, error)) {
482 ALOGW("Failed to generate device info prepared sql insert query %s", error.c_str());
483 sqlite3_finalize(stmt);
484 sqlite3_close(db);
485 return false;
486 }
487
488 if (sqlite3_step(stmt) != SQLITE_DONE) {
489 error = sqlite3_errmsg(db);
490 ALOGW("Failed to insert data to device info table: %s", error.c_str());
491 sqlite3_finalize(stmt);
492 sqlite3_close(db);
493 return false;
494 }
495 sqlite3_finalize(stmt);
496 sqlite3_close(db);
497 return true;
498 }
499 } // namespace dbutils
500 } // namespace statsd
501 } // namespace os
502 } // namespace android
503