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 package android.adservices.measurement;
17 
18 import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION;
19 
20 import android.adservices.common.AdServicesOutcomeReceiver;
21 import android.adservices.common.OutcomeReceiverConverter;
22 import android.annotation.CallbackExecutor;
23 import android.annotation.FlaggedApi;
24 import android.annotation.IntDef;
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.annotation.RequiresPermission;
28 import android.app.sdksandbox.SandboxedSdkContext;
29 import android.content.Context;
30 import android.net.Uri;
31 import android.os.Build;
32 import android.os.OutcomeReceiver;
33 import android.view.InputEvent;
34 
35 import androidx.annotation.RequiresApi;
36 
37 import com.android.adservices.flags.Flags;
38 import com.android.internal.annotations.VisibleForTesting;
39 
40 import java.lang.annotation.Retention;
41 import java.lang.annotation.RetentionPolicy;
42 import java.util.Objects;
43 import java.util.concurrent.Executor;
44 
45 /** MeasurementManager provides APIs to manage source and trigger registrations. */
46 public class MeasurementManager {
47     /** @hide */
48     public static final String MEASUREMENT_SERVICE = "measurement_service";
49 
50     /**
51      * This state indicates that Measurement APIs are unavailable. Invoking them will result in an
52      * {@link UnsupportedOperationException}.
53      */
54     public static final int MEASUREMENT_API_STATE_DISABLED = 0;
55 
56     /**
57      * This state indicates that Measurement APIs are enabled.
58      */
59     public static final int MEASUREMENT_API_STATE_ENABLED = 1;
60 
61     /** @hide */
62     @Retention(RetentionPolicy.SOURCE)
63     @IntDef(
64             prefix = "MEASUREMENT_API_STATE_",
65             value = {
66                 MEASUREMENT_API_STATE_DISABLED,
67                 MEASUREMENT_API_STATE_ENABLED,
68             })
69     public @interface MeasurementApiState {}
70 
71     private MeasurementCompatibleManager mImpl;
72 
73     /**
74      * Factory method for creating an instance of MeasurementManager.
75      *
76      * @param context The {@link Context} to use
77      * @return A {@link MeasurementManager} instance
78      */
79     @NonNull
get(@onNull Context context)80     public static MeasurementManager get(@NonNull Context context) {
81         // On T+, context.getSystemService() does more than just call constructor.
82         return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
83                 ? context.getSystemService(MeasurementManager.class)
84                 : new MeasurementManager(context);
85     }
86 
87     /**
88      * Create MeasurementManager.
89      *
90      * @hide
91      */
MeasurementManager(Context context)92     public MeasurementManager(Context context) {
93         // In case the MeasurementManager is initiated from inside a sdk_sandbox process the
94         // fields will be immediately rewritten by the initialize method below.
95         initialize(context);
96     }
97 
98     /**
99      * Create MeasurementManager
100      *
101      * @param compatibleManager the underlying implementation that can be mocked for tests
102      * @hide
103      */
104     @VisibleForTesting
MeasurementManager(@onNull MeasurementCompatibleManager compatibleManager)105     public MeasurementManager(@NonNull MeasurementCompatibleManager compatibleManager) {
106         Objects.requireNonNull(compatibleManager);
107         mImpl = compatibleManager;
108     }
109 
110     /**
111      * Initializes {@link MeasurementManager} with the given {@code context}.
112      *
113      * <p>This method is called by the {@link SandboxedSdkContext} to propagate the correct context.
114      * For more information check the javadoc on the {@link
115      * android.app.sdksandbox.SdkSandboxSystemServiceRegistry}.
116      *
117      * @hide
118      * @see android.app.sdksandbox.SdkSandboxSystemServiceRegistry
119      */
initialize(@onNull Context context)120     public MeasurementManager initialize(@NonNull Context context) {
121         mImpl = MeasurementCompatibleManager.get(context);
122         return this;
123     }
124 
125     /**
126      * Register an attribution source (click or view).
127      *
128      * @param attributionSource the platform issues a request to this URI in order to fetch metadata
129      *     associated with the attribution source. The source metadata is stored on device, making
130      *     it eligible to be matched to future triggers.
131      * @param inputEvent either an {@link InputEvent} object (for a click event) or null (for a view
132      *     event).
133      * @param executor used by callback to dispatch results.
134      * @param callback intended to notify asynchronously the API result.
135      * @throws IllegalArgumentException if the scheme for {@code attributionSource} is not HTTPS
136      */
137     @RequiresApi(Build.VERSION_CODES.S)
138     @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
registerSource( @onNull Uri attributionSource, @Nullable InputEvent inputEvent, @Nullable @CallbackExecutor Executor executor, @Nullable OutcomeReceiver<Object, Exception> callback)139     public void registerSource(
140             @NonNull Uri attributionSource,
141             @Nullable InputEvent inputEvent,
142             @Nullable @CallbackExecutor Executor executor,
143             @Nullable OutcomeReceiver<Object, Exception> callback) {
144         mImpl.registerSource(
145                 attributionSource,
146                 inputEvent,
147                 executor,
148                 OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
149     }
150 
151     /**
152      * Register an attribution source (click or view). For use on Android R or lower.
153      *
154      * @param attributionSource the platform issues a request to this URI in order to fetch metadata
155      *     associated with the attribution source. The source metadata is stored on device, making
156      *     it eligible to be matched to future triggers.
157      * @param inputEvent either an {@link InputEvent} object (for a click event) or null (for a view
158      *     event).
159      * @param executor used by callback to dispatch results.
160      * @param callback intended to notify asynchronously the API result.
161      */
162     @FlaggedApi(Flags.FLAG_ADSERVICES_OUTCOMERECEIVER_R_API_ENABLED)
163     @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
registerSource( @onNull Uri attributionSource, @Nullable InputEvent inputEvent, @Nullable @CallbackExecutor Executor executor, @Nullable AdServicesOutcomeReceiver<Object, Exception> callback)164     public void registerSource(
165             @NonNull Uri attributionSource,
166             @Nullable InputEvent inputEvent,
167             @Nullable @CallbackExecutor Executor executor,
168             @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
169         mImpl.registerSource(attributionSource, inputEvent, executor, callback);
170     }
171 
172     /**
173      * Register attribution sources(click or view) from an app context. This API will not process
174      * any redirects, all registration URLs should be supplied with the request.
175      *
176      * @param request app source registration request
177      * @param executor used by callback to dispatch results
178      * @param callback intended to notify asynchronously the API result
179      */
180     @RequiresApi(Build.VERSION_CODES.S)
181     @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
registerSource( @onNull SourceRegistrationRequest request, @Nullable @CallbackExecutor Executor executor, @Nullable OutcomeReceiver<Object, Exception> callback)182     public void registerSource(
183             @NonNull SourceRegistrationRequest request,
184             @Nullable @CallbackExecutor Executor executor,
185             @Nullable OutcomeReceiver<Object, Exception> callback) {
186         mImpl.registerSource(
187                 request, executor, OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
188     }
189 
190     /**
191      * Register attribution sources(click or view) from an app context. This API will not process
192      * any redirects, all registration URLs should be supplied with the request. For use on Android
193      * R or lower.
194      *
195      * @param request app source registration request
196      * @param executor used by callback to dispatch results
197      * @param callback intended to notify asynchronously the API result
198      */
199     @FlaggedApi(Flags.FLAG_ADSERVICES_OUTCOMERECEIVER_R_API_ENABLED)
200     @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
registerSource( @onNull SourceRegistrationRequest request, @Nullable @CallbackExecutor Executor executor, @Nullable AdServicesOutcomeReceiver<Object, Exception> callback)201     public void registerSource(
202             @NonNull SourceRegistrationRequest request,
203             @Nullable @CallbackExecutor Executor executor,
204             @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
205         mImpl.registerSource(request, executor, callback);
206     }
207 
208     /**
209      * Register an attribution source(click or view) from web context. This API will not process any
210      * redirects, all registration URLs should be supplied with the request. At least one of
211      * appDestination or webDestination parameters are required to be provided. If the registration
212      * is successful, {@code callback}'s {@link OutcomeReceiver#onResult} is invoked with null. In
213      * case of failure, a {@link Exception} is sent through {@code callback}'s {@link
214      * OutcomeReceiver#onError}. Both success and failure feedback are executed on the provided
215      * {@link Executor}.
216      *
217      * @param request source registration request
218      * @param executor used by callback to dispatch results.
219      * @param callback intended to notify asynchronously the API result.
220      */
221     @RequiresApi(Build.VERSION_CODES.S)
222     @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
registerWebSource( @onNull WebSourceRegistrationRequest request, @Nullable Executor executor, @Nullable OutcomeReceiver<Object, Exception> callback)223     public void registerWebSource(
224             @NonNull WebSourceRegistrationRequest request,
225             @Nullable Executor executor,
226             @Nullable OutcomeReceiver<Object, Exception> callback) {
227         mImpl.registerWebSource(
228                 request, executor, OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
229     }
230 
231     /**
232      * Register an attribution source(click or view) from web context. This API will not process any
233      * redirects, all registration URLs should be supplied with the request. At least one of
234      * appDestination or webDestination parameters are required to be provided. If the registration
235      * is successful, {@code callback}'s {@link OutcomeReceiver#onResult} is invoked with null. In
236      * case of failure, a {@link Exception} is sent through {@code callback}'s {@link
237      * OutcomeReceiver#onError}. Both success and failure feedback are executed on the provided
238      * {@link Executor}.
239      *
240      * <p>For use on Android R or lower.
241      *
242      * @param request source registration request
243      * @param executor used by callback to dispatch results.
244      * @param callback intended to notify asynchronously the API result.
245      */
246     @FlaggedApi(Flags.FLAG_ADSERVICES_OUTCOMERECEIVER_R_API_ENABLED)
247     @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
registerWebSource( @onNull WebSourceRegistrationRequest request, @Nullable Executor executor, @Nullable AdServicesOutcomeReceiver<Object, Exception> callback)248     public void registerWebSource(
249             @NonNull WebSourceRegistrationRequest request,
250             @Nullable Executor executor,
251             @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
252         mImpl.registerWebSource(request, executor, callback);
253     }
254 
255     /**
256      * Register an attribution trigger(click or view) from web context. This API will not process
257      * any redirects, all registration URLs should be supplied with the request. If the registration
258      * is successful, {@code callback}'s {@link OutcomeReceiver#onResult} is invoked with null. In
259      * case of failure, a {@link Exception} is sent through {@code callback}'s {@link
260      * OutcomeReceiver#onError}. Both success and failure feedback are executed on the provided
261      * {@link Executor}.
262      *
263      * @param request trigger registration request
264      * @param executor used by callback to dispatch results
265      * @param callback intended to notify asynchronously the API result
266      */
267     @RequiresApi(Build.VERSION_CODES.S)
268     @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
registerWebTrigger( @onNull WebTriggerRegistrationRequest request, @Nullable Executor executor, @Nullable OutcomeReceiver<Object, Exception> callback)269     public void registerWebTrigger(
270             @NonNull WebTriggerRegistrationRequest request,
271             @Nullable Executor executor,
272             @Nullable OutcomeReceiver<Object, Exception> callback) {
273         mImpl.registerWebTrigger(
274                 request, executor, OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
275     }
276 
277     /**
278      * Register an attribution trigger(click or view) from web context. This API will not process
279      * any redirects, all registration URLs should be supplied with the request. If the registration
280      * is successful, {@code callback}'s {@link OutcomeReceiver#onResult} is invoked with null. In
281      * case of failure, a {@link Exception} is sent through {@code callback}'s {@link
282      * OutcomeReceiver#onError}. Both success and failure feedback are executed on the provided
283      * {@link Executor}.
284      *
285      * <p>For use on Android R or lower.
286      *
287      * @param request trigger registration request
288      * @param executor used by callback to dispatch results
289      * @param callback intended to notify asynchronously the API result
290      */
291     @FlaggedApi(Flags.FLAG_ADSERVICES_OUTCOMERECEIVER_R_API_ENABLED)
292     @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
registerWebTrigger( @onNull WebTriggerRegistrationRequest request, @Nullable Executor executor, @Nullable AdServicesOutcomeReceiver<Object, Exception> callback)293     public void registerWebTrigger(
294             @NonNull WebTriggerRegistrationRequest request,
295             @Nullable Executor executor,
296             @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
297         mImpl.registerWebTrigger(request, executor, callback);
298     }
299 
300     /**
301      * Register a trigger (conversion).
302      *
303      * @param trigger the API issues a request to this URI to fetch metadata associated with the
304      *     trigger. The trigger metadata is stored on-device, and is eligible to be matched with
305      *     sources during the attribution process.
306      * @param executor used by callback to dispatch results.
307      * @param callback intended to notify asynchronously the API result.
308      * @throws IllegalArgumentException if the scheme for {@code trigger} is not HTTPS
309      */
310     @RequiresApi(Build.VERSION_CODES.S)
311     @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
registerTrigger( @onNull Uri trigger, @Nullable @CallbackExecutor Executor executor, @Nullable OutcomeReceiver<Object, Exception> callback)312     public void registerTrigger(
313             @NonNull Uri trigger,
314             @Nullable @CallbackExecutor Executor executor,
315             @Nullable OutcomeReceiver<Object, Exception> callback) {
316         mImpl.registerTrigger(
317                 trigger, executor, OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
318     }
319 
320     /**
321      * Register a trigger (conversion). For use on Android R or lower.
322      *
323      * @param trigger the API issues a request to this URI to fetch metadata associated with the
324      *     trigger. The trigger metadata is stored on-device, and is eligible to be matched with
325      *     sources during the attribution process.
326      * @param executor used by callback to dispatch results.
327      * @param callback intended to notify asynchronously the API result.
328      */
329     @FlaggedApi(Flags.FLAG_ADSERVICES_OUTCOMERECEIVER_R_API_ENABLED)
330     @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
registerTrigger( @onNull Uri trigger, @Nullable @CallbackExecutor Executor executor, @Nullable AdServicesOutcomeReceiver<Object, Exception> callback)331     public void registerTrigger(
332             @NonNull Uri trigger,
333             @Nullable @CallbackExecutor Executor executor,
334             @Nullable AdServicesOutcomeReceiver<Object, Exception> callback) {
335         mImpl.registerTrigger(trigger, executor, callback);
336     }
337 
338     /**
339      * Delete previous registrations. If the deletion is successful, the callback's {@link
340      * OutcomeReceiver#onResult} is invoked with null. In case of failure, a {@link Exception} is
341      * sent through the callback's {@link OutcomeReceiver#onError}. Both success and failure
342      * feedback are executed on the provided {@link Executor}.
343      *
344      * @param deletionRequest The request for deleting data.
345      * @param executor The executor to run callback.
346      * @param callback intended to notify asynchronously the API result.
347      */
348     @RequiresApi(Build.VERSION_CODES.S)
deleteRegistrations( @onNull DeletionRequest deletionRequest, @NonNull @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<Object, Exception> callback)349     public void deleteRegistrations(
350             @NonNull DeletionRequest deletionRequest,
351             @NonNull @CallbackExecutor Executor executor,
352             @NonNull OutcomeReceiver<Object, Exception> callback) {
353         mImpl.deleteRegistrations(
354                 deletionRequest,
355                 executor,
356                 OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
357     }
358 
359     /**
360      * Delete previous registrations. If the deletion is successful, the callback's {@link
361      * OutcomeReceiver#onResult} is invoked with null. In case of failure, a {@link Exception} is
362      * sent through the callback's {@link OutcomeReceiver#onError}. Both success and failure
363      * feedback are executed on the provided {@link Executor}.
364      *
365      * <p>For use on Android R or lower.
366      *
367      * @param deletionRequest The request for deleting data.
368      * @param executor The executor to run callback.
369      * @param callback intended to notify asynchronously the API result.
370      */
371     @FlaggedApi(Flags.FLAG_ADSERVICES_OUTCOMERECEIVER_R_API_ENABLED)
deleteRegistrations( @onNull DeletionRequest deletionRequest, @NonNull @CallbackExecutor Executor executor, @NonNull AdServicesOutcomeReceiver<Object, Exception> callback)372     public void deleteRegistrations(
373             @NonNull DeletionRequest deletionRequest,
374             @NonNull @CallbackExecutor Executor executor,
375             @NonNull AdServicesOutcomeReceiver<Object, Exception> callback) {
376         mImpl.deleteRegistrations(deletionRequest, executor, callback);
377     }
378 
379     /**
380      * Get Measurement API status.
381      *
382      * <p>The callback's {@code Integer} value is one of {@code MeasurementApiState}.
383      *
384      * @param executor used by callback to dispatch results.
385      * @param callback intended to notify asynchronously the API result.
386      */
387     @RequiresApi(Build.VERSION_CODES.S)
388     @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
getMeasurementApiStatus( @onNull @allbackExecutor Executor executor, @NonNull OutcomeReceiver<Integer, Exception> callback)389     public void getMeasurementApiStatus(
390             @NonNull @CallbackExecutor Executor executor,
391             @NonNull OutcomeReceiver<Integer, Exception> callback) {
392         mImpl.getMeasurementApiStatus(
393                 executor, OutcomeReceiverConverter.toAdServicesOutcomeReceiver(callback));
394     }
395 
396     /**
397      * Get Measurement API status.
398      *
399      * <p>The callback's {@code Integer} value is one of {@code MeasurementApiState}.
400      *
401      * <p>For use on Android R or lower.
402      *
403      * @param executor used by callback to dispatch results.
404      * @param callback intended to notify asynchronously the API result.
405      */
406     @FlaggedApi(Flags.FLAG_ADSERVICES_OUTCOMERECEIVER_R_API_ENABLED)
407     @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
getMeasurementApiStatus( @onNull @allbackExecutor Executor executor, @NonNull AdServicesOutcomeReceiver<Integer, Exception> callback)408     public void getMeasurementApiStatus(
409             @NonNull @CallbackExecutor Executor executor,
410             @NonNull AdServicesOutcomeReceiver<Integer, Exception> callback) {
411         mImpl.getMeasurementApiStatus(executor, callback);
412     }
413 
414     /**
415      * If the service is in an APK (as opposed to the system service), unbind it from the service to
416      * allow the APK process to die.
417      *
418      * @hide Not sure if we'll need this functionality in the final API. For now, we need it for
419      *     performance testing to simulate "cold-start" situations.
420      */
421     @VisibleForTesting
unbindFromService()422     public void unbindFromService() {
423         mImpl.unbindFromService();
424     }
425 }
426