1 /* 2 * Copyright (C) 2022 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 package com.android.ondevicepersonalization.services.data.events; 18 19 import android.annotation.NonNull; 20 import android.content.ComponentName; 21 import android.content.ContentValues; 22 import android.content.Context; 23 import android.database.Cursor; 24 import android.database.sqlite.SQLiteDatabase; 25 import android.database.sqlite.SQLiteException; 26 27 import com.android.internal.annotations.VisibleForTesting; 28 import com.android.ondevicepersonalization.internal.util.LoggerFactory; 29 import com.android.ondevicepersonalization.services.data.DbUtils; 30 import com.android.ondevicepersonalization.services.data.OnDevicePersonalizationDbHelper; 31 32 import java.util.ArrayList; 33 import java.util.List; 34 35 /** 36 * Dao used to manage access to Events and Queries tables 37 */ 38 public class EventsDao { 39 private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger(); 40 private static final String TAG = "EventsDao"; 41 private static final String JOINED_EVENT_TIME_MILLIS = "eventTimeMillis"; 42 private static final String JOINED_QUERY_TIME_MILLIS = "queryTimeMillis"; 43 44 private static volatile EventsDao sSingleton; 45 46 private final OnDevicePersonalizationDbHelper mDbHelper; 47 EventsDao(@onNull OnDevicePersonalizationDbHelper dbHelper)48 private EventsDao(@NonNull OnDevicePersonalizationDbHelper dbHelper) { 49 this.mDbHelper = dbHelper; 50 } 51 52 /** Returns an instance of the EventsDao given a context. */ getInstance(@onNull Context context)53 public static EventsDao getInstance(@NonNull Context context) { 54 if (sSingleton == null) { 55 synchronized (EventsDao.class) { 56 if (sSingleton == null) { 57 OnDevicePersonalizationDbHelper dbHelper = 58 OnDevicePersonalizationDbHelper.getInstance(context); 59 sSingleton = new EventsDao(dbHelper); 60 } 61 } 62 } 63 return sSingleton; 64 } 65 66 /** 67 * Returns an instance of the EventsDao given a context. This is used 68 * for testing only. 69 */ 70 @VisibleForTesting getInstanceForTest(@onNull Context context)71 public static EventsDao getInstanceForTest(@NonNull Context context) { 72 synchronized (EventsDao.class) { 73 if (sSingleton == null) { 74 OnDevicePersonalizationDbHelper dbHelper = 75 OnDevicePersonalizationDbHelper.getInstanceForTest(context); 76 sSingleton = new EventsDao(dbHelper); 77 } 78 return sSingleton; 79 } 80 } 81 82 /** 83 * Inserts the Event into the Events table. 84 * 85 * @return The row id of the newly inserted row if successful, -1 otherwise 86 */ insertEvent(@onNull Event event)87 public long insertEvent(@NonNull Event event) { 88 try { 89 SQLiteDatabase db = mDbHelper.getWritableDatabase(); 90 ContentValues values = new ContentValues(); 91 values.put(EventsContract.EventsEntry.QUERY_ID, event.getQueryId()); 92 values.put(EventsContract.EventsEntry.ROW_INDEX, event.getRowIndex()); 93 values.put(EventsContract.EventsEntry.TIME_MILLIS, event.getTimeMillis()); 94 values.put(EventsContract.EventsEntry.SERVICE_NAME, 95 event.getServiceName()); 96 values.put(EventsContract.EventsEntry.TYPE, event.getType()); 97 values.put(EventsContract.EventsEntry.EVENT_DATA, event.getEventData()); 98 return db.insert(EventsContract.EventsEntry.TABLE_NAME, null, 99 values); 100 } catch (SQLiteException e) { 101 sLogger.e(TAG + ": Failed to insert event", e); 102 } 103 return -1; 104 } 105 106 107 /** 108 * Inserts the List of Events into the Events table. 109 * 110 * @return true if all inserts succeeded, false otherwise. 111 */ insertEvents(@onNull List<Event> events)112 public boolean insertEvents(@NonNull List<Event> events) { 113 SQLiteDatabase db = mDbHelper.safeGetWritableDatabase(); 114 if (db == null) { 115 return false; 116 } 117 118 try { 119 db.beginTransactionNonExclusive(); 120 for (Event event : events) { 121 if (insertEvent(event) == -1) { 122 return false; 123 } 124 } 125 db.setTransactionSuccessful(); 126 } catch (Exception e) { 127 sLogger.e(TAG + ": Failed to insert events", e); 128 return false; 129 } finally { 130 db.endTransaction(); 131 } 132 return true; 133 } 134 135 /** 136 * Inserts the Query into the Queries table. 137 * 138 * @return The row id of the newly inserted row if successful, -1 otherwise 139 */ insertQuery(@onNull Query query)140 public long insertQuery(@NonNull Query query) { 141 try { 142 SQLiteDatabase db = mDbHelper.getWritableDatabase(); 143 ContentValues values = new ContentValues(); 144 values.put(QueriesContract.QueriesEntry.TIME_MILLIS, query.getTimeMillis()); 145 values.put(QueriesContract.QueriesEntry.SERVICE_NAME, 146 query.getServiceName()); 147 values.put(QueriesContract.QueriesEntry.QUERY_DATA, query.getQueryData()); 148 values.put(QueriesContract.QueriesEntry.APP_PACKAGE_NAME, query.getAppPackageName()); 149 values.put(QueriesContract.QueriesEntry.SERVICE_CERT_DIGEST, 150 query.getServiceCertDigest()); 151 return db.insert(QueriesContract.QueriesEntry.TABLE_NAME, null, 152 values); 153 } catch (SQLiteException e) { 154 sLogger.e(TAG + ": Failed to insert query", e); 155 } 156 return -1; 157 } 158 159 /** 160 * Updates the eventState, adds it if it doesn't already exist. 161 * 162 * @return true if the update/insert succeeded, false otherwise 163 */ updateOrInsertEventState(EventState eventState)164 public boolean updateOrInsertEventState(EventState eventState) { 165 try { 166 SQLiteDatabase db = mDbHelper.getWritableDatabase(); 167 ContentValues values = new ContentValues(); 168 values.put(EventStateContract.EventStateEntry.TOKEN, eventState.getToken()); 169 values.put(EventStateContract.EventStateEntry.SERVICE_NAME, 170 eventState.getServiceName()); 171 values.put(EventStateContract.EventStateEntry.TASK_IDENTIFIER, 172 eventState.getTaskIdentifier()); 173 return db.insertWithOnConflict(EventStateContract.EventStateEntry.TABLE_NAME, 174 null, values, SQLiteDatabase.CONFLICT_REPLACE) != -1; 175 } catch (SQLiteException e) { 176 sLogger.e(TAG + ": Failed to update or insert eventState", e); 177 } 178 return false; 179 } 180 181 /** 182 * Updates/inserts a list of EventStates as a transaction 183 * 184 * @return true if the all the update/inserts succeeded, false otherwise 185 */ updateOrInsertEventStatesTransaction(List<EventState> eventStates)186 public boolean updateOrInsertEventStatesTransaction(List<EventState> eventStates) { 187 SQLiteDatabase db = mDbHelper.safeGetWritableDatabase(); 188 if (db == null) { 189 return false; 190 } 191 192 try { 193 db.beginTransactionNonExclusive(); 194 for (EventState eventState : eventStates) { 195 if (!updateOrInsertEventState(eventState)) { 196 return false; 197 } 198 } 199 200 db.setTransactionSuccessful(); 201 } catch (Exception e) { 202 sLogger.e(TAG + ": Failed to insert/update eventstates", e); 203 return false; 204 } finally { 205 db.endTransaction(); 206 } 207 return true; 208 } 209 210 /** 211 * Gets the eventState for the given package and task 212 * 213 * @return eventState if found, null otherwise 214 */ getEventState(String taskIdentifier, ComponentName service)215 public EventState getEventState(String taskIdentifier, ComponentName service) { 216 SQLiteDatabase db = mDbHelper.safeGetReadableDatabase(); 217 if (db == null) { 218 return null; 219 } 220 221 String selection = EventStateContract.EventStateEntry.TASK_IDENTIFIER + " = ? AND " 222 + EventStateContract.EventStateEntry.SERVICE_NAME + " = ?"; 223 String[] selectionArgs = {taskIdentifier, DbUtils.toTableValue(service)}; 224 String[] projection = {EventStateContract.EventStateEntry.TOKEN}; 225 try (Cursor cursor = db.query( 226 EventStateContract.EventStateEntry.TABLE_NAME, 227 projection, 228 selection, 229 selectionArgs, 230 /* groupBy= */ null, 231 /* having= */ null, 232 /* orderBy= */ null 233 )) { 234 if (cursor.moveToFirst()) { 235 byte[] token = cursor.getBlob(cursor.getColumnIndexOrThrow( 236 EventStateContract.EventStateEntry.TOKEN)); 237 238 return new EventState.Builder() 239 .setToken(token) 240 .setService(service) 241 .setTaskIdentifier(taskIdentifier) 242 .build(); 243 } 244 } catch (SQLiteException e) { 245 sLogger.e(TAG + ": Failed to read eventState", e); 246 } 247 return null; 248 } 249 250 /** 251 * Queries the events and queries table to return all new rows from given ids for the given 252 * package 253 * 254 * @param service Name of the service to read rows for 255 * @param fromEventId EventId to find all new rows from 256 * @param fromQueryId QueryId to find all new rows from 257 * @return List of JoinedEvents. 258 */ readAllNewRowsForPackage(ComponentName service, long fromEventId, long fromQueryId)259 public List<JoinedEvent> readAllNewRowsForPackage(ComponentName service, 260 long fromEventId, long fromQueryId) { 261 String serviceName = DbUtils.toTableValue(service); 262 // Query on the joined query & event table 263 String joinedSelection = EventsContract.EventsEntry.EVENT_ID + " > ?" 264 + " AND " + EventsContract.EventsEntry.TABLE_NAME + "." 265 + EventsContract.EventsEntry.SERVICE_NAME + " = ?"; 266 String[] joinedSelectionArgs = {String.valueOf(fromEventId), serviceName}; 267 List<JoinedEvent> joinedEventList = readJoinedTableRows(joinedSelection, 268 joinedSelectionArgs); 269 270 // Query on the queries table 271 String queriesSelection = QueriesContract.QueriesEntry.QUERY_ID + " > ?" 272 + " AND " + QueriesContract.QueriesEntry.SERVICE_NAME + " = ?"; 273 String[] queriesSelectionArgs = {String.valueOf(fromQueryId), serviceName}; 274 List<Query> queryList = readQueryRows(queriesSelection, queriesSelectionArgs); 275 for (Query query : queryList) { 276 joinedEventList.add(new JoinedEvent.Builder() 277 .setQueryId(query.getQueryId()) 278 .setQueryData(query.getQueryData()) 279 .setQueryTimeMillis(query.getTimeMillis()) 280 .setService(query.getService()) 281 .build()); 282 } 283 return joinedEventList; 284 } 285 286 /** 287 * Queries the events and queries table to return all new rows from given ids for all packages 288 * 289 * @param fromEventId EventId to find all new rows from 290 * @param fromQueryId QueryId to find all new rows from 291 * @return List of JoinedEvents. 292 */ readAllNewRows(long fromEventId, long fromQueryId)293 public List<JoinedEvent> readAllNewRows(long fromEventId, long fromQueryId) { 294 // Query on the joined query & event table 295 String joinedSelection = EventsContract.EventsEntry.EVENT_ID + " > ?"; 296 String[] joinedSelectionArgs = {String.valueOf(fromEventId)}; 297 List<JoinedEvent> joinedEventList = readJoinedTableRows(joinedSelection, 298 joinedSelectionArgs); 299 300 // Query on the queries table 301 String queriesSelection = QueriesContract.QueriesEntry.QUERY_ID + " > ?"; 302 String[] queriesSelectionArgs = {String.valueOf(fromQueryId)}; 303 List<Query> queryList = readQueryRows(queriesSelection, queriesSelectionArgs); 304 for (Query query : queryList) { 305 joinedEventList.add(new JoinedEvent.Builder() 306 .setQueryId(query.getQueryId()) 307 .setQueryData(query.getQueryData()) 308 .setQueryTimeMillis(query.getTimeMillis()) 309 .setService(query.getService()) 310 .build()); 311 } 312 return joinedEventList; 313 } 314 readQueryRows(String selection, String[] selectionArgs)315 private List<Query> readQueryRows(String selection, String[] selectionArgs) { 316 List<Query> queries = new ArrayList<>(); 317 SQLiteDatabase db = mDbHelper.safeGetReadableDatabase(); 318 if (db == null) { 319 return queries; 320 } 321 322 String orderBy = QueriesContract.QueriesEntry.QUERY_ID; 323 try (Cursor cursor = db.query( 324 QueriesContract.QueriesEntry.TABLE_NAME, 325 /* projection= */ null, 326 selection, 327 selectionArgs, 328 /* groupBy= */ null, 329 /* having= */ null, 330 orderBy 331 )) { 332 while (cursor.moveToNext()) { 333 long queryId = cursor.getLong( 334 cursor.getColumnIndexOrThrow(QueriesContract.QueriesEntry.QUERY_ID)); 335 byte[] queryData = cursor.getBlob( 336 cursor.getColumnIndexOrThrow(QueriesContract.QueriesEntry.QUERY_DATA)); 337 long timeMillis = cursor.getLong( 338 cursor.getColumnIndexOrThrow(QueriesContract.QueriesEntry.TIME_MILLIS)); 339 String serviceName = cursor.getString( 340 cursor.getColumnIndexOrThrow( 341 QueriesContract.QueriesEntry.SERVICE_NAME)); 342 String appPackageName = cursor.getString(cursor.getColumnIndexOrThrow( 343 QueriesContract.QueriesEntry.APP_PACKAGE_NAME)); 344 String certDigest = cursor.getString(cursor.getColumnIndexOrThrow( 345 QueriesContract.QueriesEntry.SERVICE_CERT_DIGEST)); 346 queries.add(new Query.Builder( 347 timeMillis, appPackageName, DbUtils.fromTableValue(serviceName), 348 certDigest, queryData) 349 .setQueryId(queryId) 350 .build()); 351 } 352 } catch (IllegalArgumentException e) { 353 sLogger.e(e, TAG + ": Failed parse resulting query"); 354 return new ArrayList<>(); 355 } 356 return queries; 357 } 358 readJoinedTableRows(String selection, String[] selectionArgs)359 private List<JoinedEvent> readJoinedTableRows(String selection, String[] selectionArgs) { 360 List<JoinedEvent> joinedEventList = new ArrayList<>(); 361 SQLiteDatabase db = mDbHelper.safeGetReadableDatabase(); 362 if (db == null) { 363 return List.of(); 364 } 365 366 String select = "SELECT " 367 + EventsContract.EventsEntry.EVENT_ID + "," 368 + EventsContract.EventsEntry.ROW_INDEX + "," 369 + EventsContract.EventsEntry.TYPE + "," 370 + EventsContract.EventsEntry.TABLE_NAME + "." 371 + EventsContract.EventsEntry.SERVICE_NAME + "," 372 + EventsContract.EventsEntry.EVENT_DATA + "," 373 + EventsContract.EventsEntry.TABLE_NAME + "." 374 + EventsContract.EventsEntry.TIME_MILLIS + " AS " + JOINED_EVENT_TIME_MILLIS + "," 375 + EventsContract.EventsEntry.TABLE_NAME + "." 376 + EventsContract.EventsEntry.QUERY_ID + "," 377 + QueriesContract.QueriesEntry.QUERY_DATA + "," 378 + QueriesContract.QueriesEntry.TABLE_NAME + "." 379 + QueriesContract.QueriesEntry.TIME_MILLIS + " AS " + JOINED_QUERY_TIME_MILLIS; 380 String from = " FROM " + EventsContract.EventsEntry.TABLE_NAME 381 + " INNER JOIN " + QueriesContract.QueriesEntry.TABLE_NAME 382 + " ON " 383 + QueriesContract.QueriesEntry.TABLE_NAME + "." 384 + QueriesContract.QueriesEntry.QUERY_ID + " = " 385 + EventsContract.EventsEntry.TABLE_NAME + "." + EventsContract.EventsEntry.QUERY_ID; 386 String where = " WHERE " + selection; 387 String orderBy = " ORDER BY " + EventsContract.EventsEntry.EVENT_ID; 388 String query = select + from + where + orderBy; 389 try (Cursor cursor = db.rawQuery(query, selectionArgs)) { 390 while (cursor.moveToNext()) { 391 long eventId = cursor.getLong( 392 cursor.getColumnIndexOrThrow(EventsContract.EventsEntry.EVENT_ID)); 393 int rowIndex = cursor.getInt( 394 cursor.getColumnIndexOrThrow(EventsContract.EventsEntry.ROW_INDEX)); 395 int type = cursor.getInt( 396 cursor.getColumnIndexOrThrow(EventsContract.EventsEntry.TYPE)); 397 String serviceName = cursor.getString( 398 cursor.getColumnIndexOrThrow( 399 EventsContract.EventsEntry.SERVICE_NAME)); 400 byte[] eventData = cursor.getBlob( 401 cursor.getColumnIndexOrThrow(EventsContract.EventsEntry.EVENT_DATA)); 402 long eventTimeMillis = cursor.getLong( 403 cursor.getColumnIndexOrThrow(JOINED_EVENT_TIME_MILLIS)); 404 long queryId = cursor.getLong( 405 cursor.getColumnIndexOrThrow(QueriesContract.QueriesEntry.QUERY_ID)); 406 byte[] queryData = cursor.getBlob( 407 cursor.getColumnIndexOrThrow(QueriesContract.QueriesEntry.QUERY_DATA)); 408 long queryTimeMillis = cursor.getLong( 409 cursor.getColumnIndexOrThrow(JOINED_QUERY_TIME_MILLIS)); 410 joinedEventList.add(new JoinedEvent.Builder() 411 .setEventId(eventId) 412 .setRowIndex(rowIndex) 413 .setType(type) 414 .setEventData(eventData) 415 .setEventTimeMillis(eventTimeMillis) 416 .setQueryId(queryId) 417 .setQueryData(queryData) 418 .setQueryTimeMillis(queryTimeMillis) 419 .setService(DbUtils.fromTableValue(serviceName)) 420 .build() 421 ); 422 } 423 } catch (IllegalArgumentException e) { 424 sLogger.e(e, TAG + ": Failed parse resulting query of join statement"); 425 return new ArrayList<>(); 426 } 427 return joinedEventList; 428 } 429 430 /** 431 * Deletes all eventStates for the given packageName 432 * 433 * @return true if the delete executed successfully, false otherwise. 434 */ deleteEventState(ComponentName service)435 public boolean deleteEventState(ComponentName service) { 436 SQLiteDatabase db = mDbHelper.safeGetWritableDatabase(); 437 if (db == null) { 438 return false; 439 } 440 441 try { 442 String selection = EventStateContract.EventStateEntry.SERVICE_NAME + " = ?"; 443 String[] selectionArgs = {DbUtils.toTableValue(service)}; 444 db.delete(EventStateContract.EventStateEntry.TABLE_NAME, selection, 445 selectionArgs); 446 } catch (Exception e) { 447 sLogger.e(e, TAG + ": Failed to delete eventState for: " + service.toString()); 448 return false; 449 } 450 return true; 451 } 452 453 /** 454 * Deletes all events and queries older than the given timestamp 455 * 456 * @return true if the delete executed successfully, false otherwise. 457 */ deleteEventsAndQueries(long timestamp)458 public boolean deleteEventsAndQueries(long timestamp) { 459 SQLiteDatabase db = mDbHelper.safeGetWritableDatabase(); 460 if (db == null) { 461 return false; 462 } 463 464 try { 465 db.beginTransactionNonExclusive(); 466 // Delete from events table first to satisfy FK requirements. 467 String eventsSelection = EventsContract.EventsEntry.TIME_MILLIS + " < ?"; 468 String[] eventsSelectionArgs = {String.valueOf(timestamp)}; 469 db.delete(EventsContract.EventsEntry.TABLE_NAME, eventsSelection, 470 eventsSelectionArgs); 471 472 // Delete from queries table older than timestamp AND have no events left. 473 String queriesSelection = QueriesContract.QueriesEntry.TIME_MILLIS + " < ?" 474 + " AND " + QueriesContract.QueriesEntry.QUERY_ID 475 + " NOT IN (SELECT " + EventsContract.EventsEntry.QUERY_ID 476 + " FROM " + EventsContract.EventsEntry.TABLE_NAME + ")"; 477 String[] queriesSelectionArgs = {String.valueOf(timestamp)}; 478 db.delete(QueriesContract.QueriesEntry.TABLE_NAME, queriesSelection, 479 queriesSelectionArgs); 480 481 db.setTransactionSuccessful(); 482 } catch (Exception e) { 483 sLogger.e(e, TAG + ": Failed to delete events and queries older than: " + timestamp); 484 return false; 485 } finally { 486 db.endTransaction(); 487 } 488 return true; 489 } 490 491 /** 492 * Reads all queries in the query table between the given timestamps. 493 * 494 * @return List of Query in the query table. 495 */ readAllQueries(long startTimeMillis, long endTimeMillis, ComponentName service)496 public List<Query> readAllQueries(long startTimeMillis, long endTimeMillis, 497 ComponentName service) { 498 String selection = QueriesContract.QueriesEntry.TIME_MILLIS + " > ?" 499 + " AND " + QueriesContract.QueriesEntry.TIME_MILLIS + " < ?" 500 + " AND " + QueriesContract.QueriesEntry.SERVICE_NAME + " = ?"; 501 String[] selectionArgs = {String.valueOf(startTimeMillis), String.valueOf( 502 endTimeMillis), DbUtils.toTableValue(service)}; 503 return readQueryRows(selection, selectionArgs); 504 } 505 506 /** 507 * Reads all ids in the event table between the given timestamps. 508 * 509 * @return List of ids in the event table. 510 */ readAllEventIds(long startTimeMillis, long endTimeMillis, ComponentName service)511 public List<Long> readAllEventIds(long startTimeMillis, long endTimeMillis, 512 ComponentName service) { 513 List<Long> idList = new ArrayList<>(); 514 try { 515 SQLiteDatabase db = mDbHelper.getReadableDatabase(); 516 String[] projection = {EventsContract.EventsEntry.EVENT_ID}; 517 String selection = EventsContract.EventsEntry.TIME_MILLIS + " > ?" 518 + " AND " + EventsContract.EventsEntry.TIME_MILLIS + " < ?" 519 + " AND " + EventsContract.EventsEntry.SERVICE_NAME + " = ?"; 520 String[] selectionArgs = {String.valueOf(startTimeMillis), String.valueOf( 521 endTimeMillis), DbUtils.toTableValue(service)}; 522 String orderBy = EventsContract.EventsEntry.EVENT_ID; 523 try (Cursor cursor = db.query( 524 EventsContract.EventsEntry.TABLE_NAME, 525 projection, 526 selection, 527 selectionArgs, 528 /* groupBy= */ null, 529 /* having= */ null, 530 orderBy 531 )) { 532 while (cursor.moveToNext()) { 533 Long id = cursor.getLong( 534 cursor.getColumnIndexOrThrow(EventsContract.EventsEntry.EVENT_ID)); 535 idList.add(id); 536 } 537 cursor.close(); 538 return idList; 539 } 540 } catch (SQLiteException e) { 541 sLogger.e(TAG + ": Failed to read event ids", e); 542 } 543 return idList; 544 } 545 546 /** 547 * Returns whether an event with (queryId, type, rowIndex, service) exists. 548 */ hasEvent(long queryId, int type, int rowIndex, ComponentName service)549 public boolean hasEvent(long queryId, int type, int rowIndex, ComponentName service) { 550 try { 551 SQLiteDatabase db = mDbHelper.getReadableDatabase(); 552 String[] projection = {EventsContract.EventsEntry.EVENT_ID}; 553 String selection = EventsContract.EventsEntry.QUERY_ID + " = ?" 554 + " AND " + EventsContract.EventsEntry.TYPE + " = ?" 555 + " AND " + EventsContract.EventsEntry.ROW_INDEX + " = ?" 556 + " AND " + EventsContract.EventsEntry.SERVICE_NAME + " = ?"; 557 String[] selectionArgs = { 558 String.valueOf(queryId), 559 String.valueOf(type), 560 String.valueOf(rowIndex), 561 DbUtils.toTableValue(service) 562 }; 563 try (Cursor cursor = db.query( 564 EventsContract.EventsEntry.TABLE_NAME, 565 projection, 566 selection, 567 selectionArgs, 568 /* groupBy= */ null, 569 /* having= */ null, 570 null 571 )) { 572 if (cursor.moveToNext()) { 573 return true; 574 } 575 } 576 } catch (SQLiteException e) { 577 sLogger.e(TAG + ": Failed to read event ids for specified queryid", e); 578 } 579 return false; 580 } 581 582 /** 583 * Reads all ids in the event table associated with the specified queryId 584 * 585 * @return List of ids in the event table. 586 */ readAllEventIdsForQuery(long queryId, ComponentName service)587 public List<Long> readAllEventIdsForQuery(long queryId, ComponentName service) { 588 List<Long> idList = new ArrayList<>(); 589 try { 590 SQLiteDatabase db = mDbHelper.getReadableDatabase(); 591 String[] projection = {EventsContract.EventsEntry.EVENT_ID}; 592 String selection = EventsContract.EventsEntry.QUERY_ID + " = ?" 593 + " AND " + EventsContract.EventsEntry.SERVICE_NAME + " = ?"; 594 String[] selectionArgs = {String.valueOf(queryId), DbUtils.toTableValue(service)}; 595 String orderBy = EventsContract.EventsEntry.EVENT_ID; 596 try (Cursor cursor = db.query( 597 EventsContract.EventsEntry.TABLE_NAME, 598 projection, 599 selection, 600 selectionArgs, 601 /* groupBy= */ null, 602 /* having= */ null, 603 orderBy 604 )) { 605 while (cursor.moveToNext()) { 606 Long id = cursor.getLong( 607 cursor.getColumnIndexOrThrow(EventsContract.EventsEntry.EVENT_ID)); 608 idList.add(id); 609 } 610 cursor.close(); 611 return idList; 612 } 613 } catch (SQLiteException e) { 614 sLogger.e(TAG + ": Failed to read event ids for specified queryid", e); 615 } 616 return idList; 617 } 618 619 /** 620 * Reads single row in the query table 621 * 622 * @return Query object for the single row requested 623 */ readSingleQueryRow(long queryId, ComponentName service)624 public Query readSingleQueryRow(long queryId, ComponentName service) { 625 try { 626 SQLiteDatabase db = mDbHelper.getReadableDatabase(); 627 String selection = QueriesContract.QueriesEntry.QUERY_ID + " = ?" 628 + " AND " + QueriesContract.QueriesEntry.SERVICE_NAME + " = ?"; 629 String[] selectionArgs = {String.valueOf(queryId), DbUtils.toTableValue(service)}; 630 try (Cursor cursor = db.query( 631 QueriesContract.QueriesEntry.TABLE_NAME, 632 /* projection= */ null, 633 selection, 634 selectionArgs, 635 /* groupBy= */ null, 636 /* having= */ null, 637 /* orderBy= */ null 638 )) { 639 if (cursor.getCount() < 1) { 640 sLogger.d(TAG + ": Failed to find requested id: " + queryId); 641 return null; 642 } 643 cursor.moveToNext(); 644 long id = cursor.getLong( 645 cursor.getColumnIndexOrThrow(QueriesContract.QueriesEntry.QUERY_ID)); 646 byte[] queryData = cursor.getBlob( 647 cursor.getColumnIndexOrThrow(QueriesContract.QueriesEntry.QUERY_DATA)); 648 long timeMillis = cursor.getLong( 649 cursor.getColumnIndexOrThrow(QueriesContract.QueriesEntry.TIME_MILLIS)); 650 String appPackageName = cursor.getString(cursor.getColumnIndexOrThrow( 651 QueriesContract.QueriesEntry.APP_PACKAGE_NAME)); 652 String certDigest = cursor.getString(cursor.getColumnIndexOrThrow( 653 QueriesContract.QueriesEntry.SERVICE_CERT_DIGEST)); 654 String serviceName = cursor.getString( 655 cursor.getColumnIndexOrThrow( 656 QueriesContract.QueriesEntry.SERVICE_NAME)); 657 return new Query.Builder( 658 timeMillis, appPackageName, service, certDigest, queryData) 659 .setQueryId(id) 660 .build(); 661 } 662 } catch (SQLiteException e) { 663 sLogger.e(TAG + ": Failed to read query row", e); 664 } 665 return null; 666 } 667 668 /** 669 * Reads single row in the event table joined with its corresponding query 670 * 671 * @return JoinedEvent representing the event joined with its query 672 */ readSingleJoinedTableRow(long eventId, ComponentName service)673 public JoinedEvent readSingleJoinedTableRow(long eventId, ComponentName service) { 674 String selection = EventsContract.EventsEntry.EVENT_ID + " = ?" 675 + " AND " + EventsContract.EventsEntry.TABLE_NAME + "." 676 + EventsContract.EventsEntry.SERVICE_NAME + " = ?"; 677 String[] selectionArgs = {String.valueOf(eventId), DbUtils.toTableValue(service)}; 678 List<JoinedEvent> joinedEventList = readJoinedTableRows(selection, selectionArgs); 679 if (joinedEventList.size() < 1) { 680 sLogger.d(TAG + ": Failed to find requested id: " + eventId); 681 return null; 682 } 683 return joinedEventList.get(0); 684 } 685 686 /** 687 * Reads all row in the event table joined with its corresponding query within the given time 688 * range. 689 * 690 * @return List of JoinedEvents representing the event joined with its query 691 */ readJoinedTableRows(long startTimeMillis, long endTimeMillis, ComponentName service)692 public List<JoinedEvent> readJoinedTableRows(long startTimeMillis, long endTimeMillis, 693 ComponentName service) { 694 String selection = JOINED_EVENT_TIME_MILLIS + " > ?" 695 + " AND " + JOINED_EVENT_TIME_MILLIS + " < ?" 696 + " AND " + EventsContract.EventsEntry.TABLE_NAME + "." 697 + EventsContract.EventsEntry.SERVICE_NAME + " = ?"; 698 String[] selectionArgs = {String.valueOf(startTimeMillis), String.valueOf( 699 endTimeMillis), DbUtils.toTableValue(service)}; 700 return readJoinedTableRows(selection, selectionArgs); 701 } 702 } 703