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.Constants; 20 import android.adservices.ondevicepersonalization.EventLogRecord; 21 import android.adservices.ondevicepersonalization.RequestLogRecord; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.ComponentName; 25 import android.content.ContentValues; 26 import android.content.Context; 27 import android.os.SystemClock; 28 29 import com.android.odp.module.common.PackageUtils; 30 import com.android.ondevicepersonalization.internal.util.LoggerFactory; 31 import com.android.ondevicepersonalization.services.OnDevicePersonalizationApplication; 32 import com.android.ondevicepersonalization.services.data.DbUtils; 33 import com.android.ondevicepersonalization.services.data.events.Event; 34 import com.android.ondevicepersonalization.services.data.events.EventsDao; 35 import com.android.ondevicepersonalization.services.data.events.Query; 36 import com.android.ondevicepersonalization.services.statsd.OdpStatsdLogger; 37 38 import com.google.common.util.concurrent.Futures; 39 import com.google.common.util.concurrent.ListenableFuture; 40 41 import java.util.ArrayList; 42 import java.util.List; 43 44 /** Utilities for logging */ 45 public class LogUtils { 46 private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger(); 47 private static final String TAG = "LogUtils"; 48 49 /** Writes the provided records to the REQUESTS and EVENTS tables. */ writeLogRecords( int taskType, @NonNull Context context, @NonNull String appPackageName, @NonNull ComponentName service, @Nullable RequestLogRecord requestLogRecord, @Nullable List<EventLogRecord> eventLogRecords)50 public static ListenableFuture<Long> writeLogRecords( 51 int taskType, 52 @NonNull Context context, 53 @NonNull String appPackageName, 54 @NonNull ComponentName service, 55 @Nullable RequestLogRecord requestLogRecord, 56 @Nullable List<EventLogRecord> eventLogRecords) { 57 long logStartedTimeMills = SystemClock.elapsedRealtime(); 58 sLogger.d(TAG + ": writeLogRecords() started."); 59 try { 60 String serviceName = DbUtils.toTableValue(service); 61 String certDigest = 62 PackageUtils.getCertDigest( 63 OnDevicePersonalizationApplication.getAppContext(), 64 service.getPackageName()); 65 EventsDao eventsDao = EventsDao.getInstance(context); 66 // Insert query 67 long queryId = -1; 68 if (requestLogRecord != null) { 69 List<ContentValues> rows = requestLogRecord.getRows(); 70 if (rows.isEmpty()) { 71 rows = List.of(new ContentValues()); 72 logTraceEventStats( 73 taskType, 74 Constants.EVENT_TYPE_WRITE_REQUEST_LOG, 75 Constants.STATUS_REQUEST_LOG_IS_EMPTY, 76 SystemClock.elapsedRealtime() - logStartedTimeMills, 77 service.getPackageName()); 78 } 79 byte[] queryData = OnDevicePersonalizationFlatbufferUtils.createQueryData( 80 serviceName, certDigest, rows); 81 Query query = new Query.Builder( 82 System.currentTimeMillis(), 83 appPackageName, 84 service, 85 certDigest, 86 queryData).build(); 87 queryId = eventsDao.insertQuery(query); 88 if (queryId == -1) { 89 logTraceEventStats( 90 taskType, 91 Constants.EVENT_TYPE_WRITE_REQUEST_LOG, 92 Constants.STATUS_LOG_DB_FAILURE, 93 SystemClock.elapsedRealtime() - logStartedTimeMills, 94 service.getPackageName()); 95 return Futures.immediateFailedFuture( 96 new RuntimeException("Failed to insert request log record.")); 97 } else { 98 logTraceEventStats( 99 taskType, 100 Constants.EVENT_TYPE_WRITE_REQUEST_LOG, 101 Constants.STATUS_REQUEST_LOG_DB_SUCCESS, 102 SystemClock.elapsedRealtime() - logStartedTimeMills, 103 service.getPackageName()); 104 } 105 } else { 106 logTraceEventStats( 107 taskType, 108 Constants.EVENT_TYPE_WRITE_REQUEST_LOG, 109 Constants.STATUS_REQUEST_LOG_IS_NULL, 110 SystemClock.elapsedRealtime() - logStartedTimeMills, 111 service.getPackageName()); 112 } 113 114 // Insert events 115 if (eventLogRecords == null || eventLogRecords.size() == 0) { 116 logTraceEventStats( 117 taskType, 118 Constants.EVENT_TYPE_WRITE_EVENT_LOG, 119 Constants.STATUS_EVENT_LOG_IS_NULL, 120 SystemClock.elapsedRealtime() - logStartedTimeMills, 121 service.getPackageName()); 122 return Futures.immediateFuture(queryId); 123 } 124 List<Event> events = new ArrayList<>(); 125 for (EventLogRecord eventLogRecord : eventLogRecords) { 126 RequestLogRecord parent; 127 long parentRequestId; 128 if (eventLogRecord.getRequestLogRecord() != null) { 129 parent = eventLogRecord.getRequestLogRecord(); 130 parentRequestId = parent.getRequestId(); 131 } else { 132 parent = requestLogRecord; 133 parentRequestId = queryId; 134 } 135 // Verify requestLogRecord exists and has the corresponding rowIndex 136 if (parent == null || parentRequestId <= 0 137 || eventLogRecord.getRowIndex() >= parent.getRows().size()) { 138 logTraceEventStats( 139 taskType, 140 Constants.EVENT_TYPE_WRITE_EVENT_LOG, 141 Constants.STATUS_EVENT_LOG_NOT_EXIST, 142 SystemClock.elapsedRealtime() - logStartedTimeMills, 143 service.getPackageName()); 144 continue; 145 } 146 // Make sure query exists for package in QUERY table and 147 // rowIndex <= written row count. 148 Query queryRow = eventsDao.readSingleQueryRow(parentRequestId, service); 149 if (queryRow == null || eventLogRecord.getRowIndex() 150 >= OnDevicePersonalizationFlatbufferUtils 151 .getContentValuesLengthFromQueryData( 152 queryRow.getQueryData())) { 153 logTraceEventStats( 154 taskType, 155 Constants.EVENT_TYPE_WRITE_EVENT_LOG, 156 Constants.STATUS_EVENT_LOG_QUERY_NOT_EXIST, 157 SystemClock.elapsedRealtime() - logStartedTimeMills, 158 service.getPackageName()); 159 continue; 160 } 161 Event event = new Event.Builder() 162 .setEventData(OnDevicePersonalizationFlatbufferUtils.createEventData( 163 eventLogRecord.getData())) 164 .setQueryId(parentRequestId) 165 .setRowIndex(eventLogRecord.getRowIndex()) 166 .setService(service) 167 .setTimeMillis(System.currentTimeMillis()) 168 .setType(eventLogRecord.getType()) 169 .build(); 170 events.add(event); 171 } 172 if (!eventsDao.insertEvents(events)) { 173 logTraceEventStats( 174 taskType, 175 Constants.EVENT_TYPE_WRITE_EVENT_LOG, 176 Constants.STATUS_LOG_DB_FAILURE, 177 SystemClock.elapsedRealtime() - logStartedTimeMills, 178 service.getPackageName()); 179 return Futures.immediateFailedFuture( 180 new RuntimeException("Failed to insert events log record.")); 181 } else { 182 logTraceEventStats( 183 taskType, 184 Constants.EVENT_TYPE_WRITE_EVENT_LOG, 185 Constants.STATUS_EVENT_LOG_DB_SUCCESS, 186 SystemClock.elapsedRealtime() - logStartedTimeMills, 187 service.getPackageName()); 188 } 189 return Futures.immediateFuture(queryId); 190 } catch (Exception e) { 191 logTraceEventStats( 192 taskType, 193 Constants.EVENT_TYPE_UNKNOWN, 194 Constants.STATUS_LOG_EXCEPTION, 195 SystemClock.elapsedRealtime() - logStartedTimeMills, 196 service.getPackageName()); 197 return Futures.immediateFailedFuture(e); 198 } 199 } 200 logTraceEventStats(int taskType, int eventType, int status, long latencyMillis, String servicePackageName)201 private static void logTraceEventStats(int taskType, int eventType, int status, 202 long latencyMillis, String servicePackageName) { 203 OdpStatsdLogger.getInstance() 204 .logTraceEventStats(taskType, eventType, status, latencyMillis, servicePackageName); 205 } 206 LogUtils()207 private LogUtils() {} 208 } 209