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