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.measurement;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.net.Uri;
22 import android.os.Build;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.view.InputEvent;
26 
27 import com.android.adservices.AdServicesParcelableUtil;
28 
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.Objects;
32 
33 /**
34  * Class to hold input to measurement source registration calls.
35  */
36 public final class SourceRegistrationRequest implements Parcelable {
37     private static final int REGISTRATION_URIS_MAX_COUNT = 20;
38     /** Registration URIs to fetch sources. */
39     @NonNull private final List<Uri> mRegistrationUris;
40 
41     /**
42      * User Interaction {@link InputEvent} used by the AttributionReporting API to distinguish
43      * clicks from views. It will be an {@link InputEvent} object (for a click event) or null (for a
44      * view event).
45      */
46     @Nullable private final InputEvent mInputEvent;
47 
SourceRegistrationRequest(@onNull Builder builder)48     private SourceRegistrationRequest(@NonNull Builder builder) {
49         mRegistrationUris = builder.mRegistrationUris;
50         mInputEvent = builder.mInputEvent;
51     }
52 
SourceRegistrationRequest(@onNull Parcel in)53     private SourceRegistrationRequest(@NonNull Parcel in) {
54         Objects.requireNonNull(in);
55         List<Uri> registrationsUris = new ArrayList<>();
56         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
57             in.readList(registrationsUris, Uri.class.getClassLoader());
58         } else {
59             in.readList(registrationsUris, Uri.class.getClassLoader(), Uri.class);
60         }
61         mRegistrationUris = registrationsUris;
62         mInputEvent =
63                 AdServicesParcelableUtil.readNullableFromParcel(
64                         in, InputEvent.CREATOR::createFromParcel);
65     }
66 
67     @Override
equals(Object o)68     public boolean equals(Object o) {
69         if (this == o) return true;
70         if (!(o instanceof SourceRegistrationRequest)) return false;
71         SourceRegistrationRequest that = (SourceRegistrationRequest) o;
72         return Objects.equals(mRegistrationUris, that.mRegistrationUris)
73                 && Objects.equals(mInputEvent, that.mInputEvent);
74     }
75 
76     @Override
hashCode()77     public int hashCode() {
78         return Objects.hash(mRegistrationUris, mInputEvent);
79     }
80 
81     /** Registration URIs to fetch sources. */
82     @NonNull
getRegistrationUris()83     public List<Uri> getRegistrationUris() {
84         return mRegistrationUris;
85     }
86 
87     /**
88      * User Interaction {@link InputEvent} used by the AttributionReporting API to distinguish
89      * clicks from views. It will be an {@link InputEvent} object (for a click event) or null (for a
90      * view event)
91      */
92     @Nullable
getInputEvent()93     public InputEvent getInputEvent() {
94         return mInputEvent;
95     }
96 
97     @Override
describeContents()98     public int describeContents() {
99         return 0;
100     }
101 
102     @Override
writeToParcel(@onNull Parcel out, int flags)103     public void writeToParcel(@NonNull Parcel out, int flags) {
104         Objects.requireNonNull(out);
105         out.writeList(mRegistrationUris);
106         AdServicesParcelableUtil.writeNullableToParcel(
107                 out, mInputEvent, (target, event) -> event.writeToParcel(target, flags));
108     }
109 
110     /** Builder for {@link SourceRegistrationRequest}. */
111     public static final class Builder {
112         /** Registration {@link Uri}s to fetch sources. */
113         @NonNull private final List<Uri> mRegistrationUris;
114         /**
115          * User Interaction InputEvent used by the attribution reporting API to distinguish clicks
116          * from views.
117          */
118         @Nullable private InputEvent mInputEvent;
119 
120         /**
121          * Builder constructor for {@link SourceRegistrationRequest}.
122          *
123          * @param registrationUris source registration {@link Uri}s
124          * @throws IllegalArgumentException if the scheme for one or more of the
125          * {@code registrationUris} is not HTTPS
126          */
Builder(@onNull List<Uri> registrationUris)127         public Builder(@NonNull List<Uri> registrationUris) {
128             Objects.requireNonNull(registrationUris);
129             if (registrationUris.isEmpty()
130                     || registrationUris.size() > REGISTRATION_URIS_MAX_COUNT) {
131                 throw new IllegalArgumentException(
132                         String.format(
133                                 "Requests should have at least 1 and at most %d URIs."
134                                         + " Request has %d URIs.",
135                                 REGISTRATION_URIS_MAX_COUNT, registrationUris.size()));
136             }
137             for (Uri registrationUri : registrationUris) {
138                 if (registrationUri.getScheme() == null
139                         || !registrationUri.getScheme().equalsIgnoreCase("https")) {
140                     throw new IllegalArgumentException(
141                             "registrationUri must have an HTTPS scheme");
142                 }
143             }
144             mRegistrationUris = registrationUris;
145         }
146 
147         /**
148          * Setter corresponding to {@link #getInputEvent()}.
149          *
150          * @param inputEvent User Interaction {@link InputEvent} used by the AttributionReporting
151          *     API to distinguish clicks from views. It will be an {@link InputEvent} object (for a
152          *     click event) or null (for a view event)
153          * @return builder
154          */
155         @NonNull
setInputEvent(@ullable InputEvent inputEvent)156         public Builder setInputEvent(@Nullable InputEvent inputEvent) {
157             mInputEvent = inputEvent;
158             return this;
159         }
160 
161         /** Pre-validates parameters and builds {@link SourceRegistrationRequest}. */
162         @NonNull
build()163         public SourceRegistrationRequest build() {
164             return new SourceRegistrationRequest(this);
165         }
166     }
167 
168     /** Creator for Paracelable (via reflection). */
169     @NonNull
170     public static final Parcelable.Creator<SourceRegistrationRequest> CREATOR =
171             new Parcelable.Creator<>() {
172                 @Override
173                 public SourceRegistrationRequest createFromParcel(Parcel in) {
174                     return new SourceRegistrationRequest(in);
175                 }
176 
177                 @Override
178                 public SourceRegistrationRequest[] newArray(int size) {
179                     return new SourceRegistrationRequest[size];
180                 }
181             };
182 }
183