• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.adservices.ondevicepersonalization;
18 
19 import android.adservices.ondevicepersonalization.aidl.IDataAccessService;
20 import android.adservices.ondevicepersonalization.aidl.IDataAccessServiceCallback;
21 import android.annotation.NonNull;
22 import android.annotation.WorkerThread;
23 import android.os.Bundle;
24 import android.os.Parcelable;
25 import android.os.RemoteException;
26 
27 import com.android.ondevicepersonalization.internal.util.LoggerFactory;
28 import com.android.ondevicepersonalization.internal.util.OdpParceledListSlice;
29 
30 import java.time.Instant;
31 import java.util.List;
32 import java.util.Objects;
33 import java.util.concurrent.ArrayBlockingQueue;
34 import java.util.concurrent.BlockingQueue;
35 
36 /**
37  * An interface to a read logs from REQUESTS and EVENTS
38  *
39  * Used as a Data Access Object for the REQUESTS and EVENTS table.
40  *
41  * @see IsolatedService#getLogReader(RequestToken)
42  *
43  */
44 public class LogReader {
45     private static final String TAG = "LogReader";
46     private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
47 
48     @NonNull
49     private final IDataAccessService mDataAccessService;
50 
51     /** @hide */
LogReader(@onNull IDataAccessService binder)52     public LogReader(@NonNull IDataAccessService binder) {
53         mDataAccessService = Objects.requireNonNull(binder);
54     }
55 
56 
57     /**
58      * Retrieves a List of RequestLogRecords written by this IsolatedService within
59      * the specified time range.
60      */
61     @WorkerThread
62     @NonNull
getRequests( @onNull Instant startTime, @NonNull Instant endTime)63     public List<RequestLogRecord> getRequests(
64             @NonNull Instant startTime, @NonNull Instant endTime) {
65         final long apiStartTimeMillis = System.currentTimeMillis();
66         int responseCode = Constants.STATUS_SUCCESS;
67         long startTimeMillis = startTime.toEpochMilli();
68         long endTimeMillis = endTime.toEpochMilli();
69         if (endTimeMillis <= startTimeMillis) {
70             throw new IllegalArgumentException(
71                     "endTimeMillis must be greater than startTimeMillis");
72         }
73         if (startTimeMillis < 0) {
74             throw new IllegalArgumentException("startTimeMillis must be greater than 0");
75         }
76         try {
77             Bundle params = new Bundle();
78             params.putLongArray(Constants.EXTRA_LOOKUP_KEYS,
79                     new long[]{startTimeMillis, endTimeMillis});
80             OdpParceledListSlice<RequestLogRecord> result =
81                     handleListLookupRequest(Constants.DATA_ACCESS_OP_GET_REQUESTS, params);
82             return result.getList();
83         } catch (RuntimeException e) {
84             responseCode = Constants.STATUS_INTERNAL_ERROR;
85             throw e;
86         } finally {
87             logApiCallStats(
88                     Constants.API_NAME_LOG_READER_GET_REQUESTS,
89                     System.currentTimeMillis() - apiStartTimeMillis,
90                     responseCode);
91         }
92     }
93 
94     /**
95      * Retrieves a List of EventLogRecord with its corresponding RequestLogRecord written by this
96      * IsolatedService within the specified time range.
97      */
98     @WorkerThread
99     @NonNull
getJoinedEvents( @onNull Instant startTime, @NonNull Instant endTime)100     public List<EventLogRecord> getJoinedEvents(
101             @NonNull Instant startTime, @NonNull Instant endTime) {
102         final long apiStartTimeMillis = System.currentTimeMillis();
103         int responseCode = Constants.STATUS_SUCCESS;
104         long startTimeMillis = startTime.toEpochMilli();
105         long endTimeMillis = endTime.toEpochMilli();
106         if (endTimeMillis <= startTimeMillis) {
107             throw new IllegalArgumentException(
108                     "endTimeMillis must be greater than startTimeMillis");
109         }
110         if (startTimeMillis < 0) {
111             throw new IllegalArgumentException("startTimeMillis must be greater than 0");
112         }
113         try {
114             Bundle params = new Bundle();
115             params.putLongArray(Constants.EXTRA_LOOKUP_KEYS,
116                     new long[]{startTimeMillis, endTimeMillis});
117             OdpParceledListSlice<EventLogRecord> result =
118                     handleListLookupRequest(Constants.DATA_ACCESS_OP_GET_JOINED_EVENTS, params);
119             return result.getList();
120         } catch (RuntimeException e) {
121             responseCode = Constants.STATUS_INTERNAL_ERROR;
122             throw e;
123         } finally {
124             logApiCallStats(
125                     Constants.API_NAME_LOG_READER_GET_JOINED_EVENTS,
126                     System.currentTimeMillis() - apiStartTimeMillis,
127                     responseCode);
128         }
129     }
130 
handleAsyncRequest(int op, Bundle params)131     private Bundle handleAsyncRequest(int op, Bundle params) {
132         try {
133             BlockingQueue<Bundle> asyncResult = new ArrayBlockingQueue<>(1);
134             mDataAccessService.onRequest(
135                     op,
136                     params,
137                     new IDataAccessServiceCallback.Stub() {
138                         @Override
139                         public void onSuccess(@NonNull Bundle result) {
140                             if (result != null) {
141                                 asyncResult.add(result);
142                             } else {
143                                 asyncResult.add(Bundle.EMPTY);
144                             }
145                         }
146 
147                         @Override
148                         public void onError(int errorCode) {
149                             asyncResult.add(Bundle.EMPTY);
150                         }
151                     });
152             return asyncResult.take();
153         } catch (InterruptedException | RemoteException e) {
154             sLogger.e(TAG + ": Failed to retrieve result", e);
155             throw new IllegalStateException(e);
156         }
157     }
158 
handleListLookupRequest(int op, Bundle params)159     private <T extends Parcelable> OdpParceledListSlice<T> handleListLookupRequest(int op,
160             Bundle params) {
161         Bundle result = handleAsyncRequest(op, params);
162         try {
163             OdpParceledListSlice<T> data = result.getParcelable(
164                     Constants.EXTRA_RESULT, OdpParceledListSlice.class);
165             if (null == data) {
166                 sLogger.e(TAG + ": No EXTRA_RESULT was present in bundle");
167                 throw new IllegalStateException("Bundle missing EXTRA_RESULT.");
168             }
169             return data;
170         } catch (ClassCastException e) {
171             throw new IllegalStateException("Failed to retrieve parceled list");
172         }
173     }
174 
logApiCallStats(int apiName, long duration, int responseCode)175     private void logApiCallStats(int apiName, long duration, int responseCode) {
176         try {
177             mDataAccessService.logApiCallStats(apiName, duration, responseCode);
178         } catch (Exception e) {
179             sLogger.d(e, TAG + ": failed to log metrics");
180         }
181     }
182 }
183