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