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 package com.android.ondevicepersonalization.services.util; 18 19 import android.adservices.ondevicepersonalization.EventLogRecord; 20 import android.adservices.ondevicepersonalization.RequestLogRecord; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.ComponentName; 24 import android.content.ContentValues; 25 import android.content.Context; 26 27 import com.android.odp.module.common.PackageUtils; 28 import com.android.ondevicepersonalization.internal.util.LoggerFactory; 29 import com.android.ondevicepersonalization.services.OnDevicePersonalizationApplication; 30 import com.android.ondevicepersonalization.services.data.DbUtils; 31 import com.android.ondevicepersonalization.services.data.events.Event; 32 import com.android.ondevicepersonalization.services.data.events.EventsDao; 33 import com.android.ondevicepersonalization.services.data.events.Query; 34 35 import com.google.common.util.concurrent.Futures; 36 import com.google.common.util.concurrent.ListenableFuture; 37 38 import java.util.ArrayList; 39 import java.util.List; 40 41 /** Utilities for logging */ 42 public class LogUtils { 43 private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger(); 44 private static final String TAG = "LogUtils"; 45 46 /** Writes the provided records to the REQUESTS and EVENTS tables. */ writeLogRecords( @onNull Context context, @NonNull String appPackageName, @NonNull ComponentName service, @Nullable RequestLogRecord requestLogRecord, @NonNull List<EventLogRecord> eventLogRecords)47 public static ListenableFuture<Long> writeLogRecords( 48 @NonNull Context context, 49 @NonNull String appPackageName, 50 @NonNull ComponentName service, 51 @Nullable RequestLogRecord requestLogRecord, 52 @NonNull List<EventLogRecord> eventLogRecords) { 53 sLogger.d(TAG + ": writeLogRecords() started."); 54 try { 55 String serviceName = DbUtils.toTableValue(service); 56 String certDigest = 57 PackageUtils.getCertDigest( 58 OnDevicePersonalizationApplication.getAppContext(), 59 service.getPackageName()); 60 EventsDao eventsDao = EventsDao.getInstance(context); 61 // Insert query 62 long queryId = -1; 63 if (requestLogRecord != null) { 64 List<ContentValues> rows = requestLogRecord.getRows(); 65 if (rows.isEmpty()) { 66 rows = List.of(new ContentValues()); 67 } 68 byte[] queryData = OnDevicePersonalizationFlatbufferUtils.createQueryData( 69 serviceName, certDigest, rows); 70 Query query = new Query.Builder( 71 System.currentTimeMillis(), 72 appPackageName, 73 service, 74 certDigest, 75 queryData).build(); 76 queryId = eventsDao.insertQuery(query); 77 if (queryId == -1) { 78 return Futures.immediateFailedFuture( 79 new RuntimeException("Failed to log query.")); 80 } 81 } 82 83 // Insert events 84 List<Event> events = new ArrayList<>(); 85 for (EventLogRecord eventLogRecord : eventLogRecords) { 86 RequestLogRecord parent; 87 long parentRequestId; 88 if (eventLogRecord.getRequestLogRecord() != null) { 89 parent = eventLogRecord.getRequestLogRecord(); 90 parentRequestId = parent.getRequestId(); 91 } else { 92 parent = requestLogRecord; 93 parentRequestId = queryId; 94 } 95 // Verify requestLogRecord exists and has the corresponding rowIndex 96 if (parent == null || parentRequestId <= 0 97 || eventLogRecord.getRowIndex() >= parent.getRows().size()) { 98 continue; 99 } 100 // Make sure query exists for package in QUERY table and 101 // rowIndex <= written row count. 102 Query queryRow = eventsDao.readSingleQueryRow(parentRequestId, service); 103 if (queryRow == null || eventLogRecord.getRowIndex() 104 >= OnDevicePersonalizationFlatbufferUtils 105 .getContentValuesLengthFromQueryData( 106 queryRow.getQueryData())) { 107 continue; 108 } 109 Event event = new Event.Builder() 110 .setEventData(OnDevicePersonalizationFlatbufferUtils.createEventData( 111 eventLogRecord.getData())) 112 .setQueryId(parentRequestId) 113 .setRowIndex(eventLogRecord.getRowIndex()) 114 .setService(service) 115 .setTimeMillis(System.currentTimeMillis()) 116 .setType(eventLogRecord.getType()) 117 .build(); 118 events.add(event); 119 } 120 if (!eventsDao.insertEvents(events)) { 121 return Futures.immediateFailedFuture( 122 new RuntimeException("Failed to log events.")); 123 } 124 125 return Futures.immediateFuture(queryId); 126 } catch (Exception e) { 127 return Futures.immediateFailedFuture(e); 128 } 129 } 130 LogUtils()131 private LogUtils() {} 132 } 133