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 android.adservices.ondevicepersonalization;
18 
19 import static android.adservices.ondevicepersonalization.OnDevicePersonalizationPermissions.NOTIFY_MEASUREMENT_EVENT;
20 
21 import android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationManagingService;
22 import android.adservices.ondevicepersonalization.aidl.IRegisterMeasurementEventCallback;
23 import android.annotation.CallbackExecutor;
24 import android.annotation.FlaggedApi;
25 import android.annotation.NonNull;
26 import android.annotation.RequiresPermission;
27 import android.annotation.SystemApi;
28 import android.content.Context;
29 import android.os.Binder;
30 import android.os.Bundle;
31 import android.os.OutcomeReceiver;
32 import android.os.SystemClock;
33 
34 import com.android.adservices.ondevicepersonalization.flags.Flags;
35 import com.android.federatedcompute.internal.util.AbstractServiceBinder;
36 import com.android.internal.annotations.VisibleForTesting;
37 import com.android.ondevicepersonalization.internal.util.LoggerFactory;
38 
39 import java.util.List;
40 import java.util.Objects;
41 import java.util.concurrent.Executor;
42 
43 /**
44  * Provides APIs for the platform to signal events that are to be handled by the ODP service.
45  * @hide
46  */
47 @SystemApi
48 @FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
49 public class OnDevicePersonalizationSystemEventManager {
50     /** @hide */
51     public static final String ON_DEVICE_PERSONALIZATION_SYSTEM_EVENT_SERVICE =
52             "on_device_personalization_system_event_service";
53     private static final String INTENT_FILTER_ACTION =
54             "android.OnDevicePersonalizationService";
55     private static final String ODP_MANAGING_SERVICE_PACKAGE_SUFFIX =
56             "com.android.ondevicepersonalization.services";
57     private static final String ALT_ODP_MANAGING_SERVICE_PACKAGE_SUFFIX =
58             "com.google.android.ondevicepersonalization.services";
59     private static final String TAG =
60             OnDevicePersonalizationSystemEventManager.class.getSimpleName();
61     private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
62 
63     // TODO(b/301732670): Define a new service for this manager and bind to it.
64     private final AbstractServiceBinder<IOnDevicePersonalizationManagingService> mServiceBinder;
65     private final Context mContext;
66 
67     /** @hide */
OnDevicePersonalizationSystemEventManager(Context context)68     public OnDevicePersonalizationSystemEventManager(Context context) {
69         this(
70                 context,
71                 AbstractServiceBinder.getServiceBinderByIntent(
72                         context,
73                         INTENT_FILTER_ACTION,
74                         List.of(
75                                 ODP_MANAGING_SERVICE_PACKAGE_SUFFIX,
76                                 ALT_ODP_MANAGING_SERVICE_PACKAGE_SUFFIX),
77                         0,
78                         IOnDevicePersonalizationManagingService.Stub::asInterface));
79     }
80 
81     /** @hide */
82     @VisibleForTesting
OnDevicePersonalizationSystemEventManager( Context context, AbstractServiceBinder<IOnDevicePersonalizationManagingService> serviceBinder)83     public OnDevicePersonalizationSystemEventManager(
84             Context context,
85             AbstractServiceBinder<IOnDevicePersonalizationManagingService> serviceBinder) {
86         mContext = context;
87         mServiceBinder = serviceBinder;
88     }
89 
90     /**
91      * Receives a web trigger event from the Measurement API. This is intended to be called by the
92      * <a href="https://developer.android.com/design-for-safety/privacy-sandbox/guides/attribution">
93      * Measurement Service</a> when a browser registers an attribution event using the
94      * <a href="https://github.com/WICG/attribution-reporting-api">Attribution and Reporting API</a>
95      * with a payload that should be processed by an {@link IsolatedService}.
96      *
97      * @param measurementWebTriggerEvent the web trigger payload to be processed.
98      * @param executor the {@link Executor} on which to invoke the callback.
99      * @param receiver This either returns a {@code null} on success, or an exception on failure.
100      */
101     @RequiresPermission(NOTIFY_MEASUREMENT_EVENT)
notifyMeasurementEvent( @onNull MeasurementWebTriggerEventParams measurementWebTriggerEvent, @NonNull @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<Void, Exception> receiver)102     public void notifyMeasurementEvent(
103             @NonNull MeasurementWebTriggerEventParams measurementWebTriggerEvent,
104             @NonNull @CallbackExecutor Executor executor,
105             @NonNull OutcomeReceiver<Void, Exception> receiver) {
106         Objects.requireNonNull(measurementWebTriggerEvent);
107         Objects.requireNonNull(executor);
108         Objects.requireNonNull(receiver);
109         long startTimeMillis = SystemClock.elapsedRealtime();
110 
111         final IOnDevicePersonalizationManagingService service =
112                 mServiceBinder.getService(executor);
113 
114         try {
115             Bundle bundle = new Bundle();
116             bundle.putParcelable(Constants.EXTRA_MEASUREMENT_WEB_TRIGGER_PARAMS,
117                     new MeasurementWebTriggerEventParamsParcel(measurementWebTriggerEvent));
118             // TODO(b/301732670): Update method name in service.
119             service.registerMeasurementEvent(
120                     Constants.MEASUREMENT_EVENT_TYPE_WEB_TRIGGER,
121                     bundle,
122                     new CallerMetadata.Builder().setStartTimeMillis(startTimeMillis).build(),
123                     new IRegisterMeasurementEventCallback.Stub() {
124                         @Override
125                         public void onSuccess(CalleeMetadata calleeMetadata) {
126                             final long token = Binder.clearCallingIdentity();
127                             try {
128                                 executor.execute(() -> receiver.onResult(null));
129                             } finally {
130                                 Binder.restoreCallingIdentity(token);
131                                 logApiCallStats(
132                                         service,
133                                         "",
134                                         Constants.API_NAME_NOTIFY_MEASUREMENT_EVENT,
135                                         SystemClock.elapsedRealtime() - startTimeMillis,
136                                         calleeMetadata.getServiceEntryTimeMillis() - startTimeMillis,
137                                         SystemClock.elapsedRealtime()
138                                                 - calleeMetadata.getCallbackInvokeTimeMillis(),
139                                         Constants.STATUS_SUCCESS);
140                             }
141                         }
142                         @Override
143                         public void onError(int errorCode, CalleeMetadata calleeMetadata) {
144                             final long token = Binder.clearCallingIdentity();
145                             try {
146                                 executor.execute(() -> receiver.onError(
147                                         new IllegalStateException("Error: " + errorCode)));
148                             } finally {
149                                 Binder.restoreCallingIdentity(token);
150                                 logApiCallStats(
151                                         service,
152                                         "",
153                                         Constants.API_NAME_NOTIFY_MEASUREMENT_EVENT,
154                                         SystemClock.elapsedRealtime() - startTimeMillis,
155                                         calleeMetadata.getServiceEntryTimeMillis() - startTimeMillis,
156                                         SystemClock.elapsedRealtime()
157                                                 - calleeMetadata.getCallbackInvokeTimeMillis(),
158                                         errorCode);
159                             }
160                         }
161                     }
162             );
163         } catch (IllegalArgumentException | NullPointerException e) {
164             logApiCallStats(
165                     service,
166                     "",
167                     Constants.API_NAME_NOTIFY_MEASUREMENT_EVENT,
168                     SystemClock.elapsedRealtime() - startTimeMillis,
169                     0,
170                     0,
171                     Constants.STATUS_INTERNAL_ERROR);
172             throw e;
173         } catch (Exception e) {
174             logApiCallStats(
175                     service,
176                     "",
177                     Constants.API_NAME_NOTIFY_MEASUREMENT_EVENT,
178                     SystemClock.elapsedRealtime() - startTimeMillis,
179                     0,
180                     0,
181                     Constants.STATUS_INTERNAL_ERROR);
182             receiver.onError(e);
183         }
184     }
185 
logApiCallStats( IOnDevicePersonalizationManagingService service, String sdkPackageName, int apiName, long latencyMillis, long rpcCallLatencyMillis, long rpcReturnLatencyMillis, int responseCode)186     private void logApiCallStats(
187             IOnDevicePersonalizationManagingService service,
188             String sdkPackageName,
189             int apiName,
190             long latencyMillis,
191             long rpcCallLatencyMillis,
192             long rpcReturnLatencyMillis,
193             int responseCode) {
194         try {
195             if (service != null) {
196                 service.logApiCallStats(sdkPackageName, apiName, latencyMillis,
197                         rpcCallLatencyMillis, rpcReturnLatencyMillis, responseCode);
198             }
199         } catch (Exception e) {
200             sLogger.e(e, TAG + ": Error logging API call stats");
201         }
202     }
203 }
204