• 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.common;
18 
19 import android.annotation.NonNull;
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 
23 import com.android.adservices.AdServicesParcelableUtil;
24 import com.android.internal.annotations.VisibleForTesting;
25 
26 import org.json.JSONArray;
27 import org.json.JSONException;
28 import org.json.JSONObject;
29 
30 import java.util.HashSet;
31 import java.util.Objects;
32 import java.util.Set;
33 
34 // TODO(b/266837113) link to setAppInstallAdvertisers once unhidden.
35 
36 /**
37  * A container for the ad filters that are based on app install state.
38  *
39  * <p>App install filters filter out ads based on the presence of packages installed on the device.
40  * In order for filtering to work, a package must call the setAppInstallAdvertisers API with the
41  * identifier of the adtech who owns this ad. If that call has been made, and the ad contains an
42  * {@link AppInstallFilters} object whose package name set contains the name of the package, the ad
43  * will be removed from the auction.
44  *
45  * <p>Note that the filtering is based on any package with one of the listed package names being on
46  * the device. It is possible that the package holding the package name is not the application
47  * targeted by the ad.
48  *
49  * @hide
50  */
51 public final class AppInstallFilters implements Parcelable {
52     /** @hide */
53     @VisibleForTesting public static final String PACKAGE_NAMES_FIELD_NAME = "package_names";
54 
55     @NonNull private final Set<String> mPackageNames;
56 
57     @NonNull
58     public static final Creator<AppInstallFilters> CREATOR =
59             new Creator<AppInstallFilters>() {
60                 @NonNull
61                 @Override
62                 public AppInstallFilters createFromParcel(@NonNull Parcel in) {
63                     Objects.requireNonNull(in);
64                     return new AppInstallFilters(in);
65                 }
66 
67                 @NonNull
68                 @Override
69                 public AppInstallFilters[] newArray(int size) {
70                     return new AppInstallFilters[size];
71                 }
72             };
73 
AppInstallFilters(@onNull Builder builder)74     private AppInstallFilters(@NonNull Builder builder) {
75         Objects.requireNonNull(builder);
76 
77         mPackageNames = builder.mPackageNames;
78     }
79 
AppInstallFilters(@onNull Parcel in)80     private AppInstallFilters(@NonNull Parcel in) {
81         Objects.requireNonNull(in);
82 
83         mPackageNames = AdServicesParcelableUtil.readStringSetFromParcel(in);
84     }
85 
86     /**
87      * Gets the list of package names this ad is filtered on.
88      *
89      * <p>The ad containing this filter will be removed from the ad auction if any of the package
90      * names are present on the device and have called setAppInstallAdvertisers.
91      */
92     @NonNull
getPackageNames()93     public Set<String> getPackageNames() {
94         return mPackageNames;
95     }
96 
97     /**
98      * @return The estimated size of this object, in bytes.
99      * @hide
100      */
getSizeInBytes()101     public int getSizeInBytes() {
102         int totalSize = 0;
103         for (String packageName : mPackageNames) {
104             totalSize += packageName.getBytes().length;
105         }
106         return totalSize;
107     }
108 
109     /**
110      * A JSON serializer.
111      *
112      * @return A JSON serialization of this object.
113      * @hide
114      */
toJson()115     public JSONObject toJson() throws JSONException {
116         JSONObject toReturn = new JSONObject();
117         JSONArray packageNames = new JSONArray();
118         for (String packageName : mPackageNames) {
119             packageNames.put(packageName);
120         }
121         toReturn.put(PACKAGE_NAMES_FIELD_NAME, packageNames);
122         return toReturn;
123     }
124 
125     /**
126      * A JSON de-serializer.
127      *
128      * @param json A JSON representation of an {@link AppInstallFilters} object as would be
129      *     generated by {@link #toJson()}.
130      * @return An {@link AppInstallFilters} object generated from the given JSON.
131      * @hide
132      */
fromJson(JSONObject json)133     public static AppInstallFilters fromJson(JSONObject json) throws JSONException {
134         JSONArray serializedPackageNames = json.getJSONArray(PACKAGE_NAMES_FIELD_NAME);
135         Set<String> packageNames = new HashSet<>();
136         for (int i = 0; i < serializedPackageNames.length(); i++) {
137             Object packageName = serializedPackageNames.get(i);
138             if (packageName instanceof String) {
139                 packageNames.add((String) packageName);
140             } else {
141                 throw new JSONException(
142                         "Found non-string package name when de-serializing AppInstallFilters");
143             }
144         }
145         return new Builder().setPackageNames(packageNames).build();
146     }
147 
148     @Override
writeToParcel(@onNull Parcel dest, int flags)149     public void writeToParcel(@NonNull Parcel dest, int flags) {
150         Objects.requireNonNull(dest);
151         AdServicesParcelableUtil.writeStringSetToParcel(dest, mPackageNames);
152     }
153 
154     /** @hide */
155     @Override
describeContents()156     public int describeContents() {
157         return 0;
158     }
159 
160     /** Checks whether the {@link AppInstallFilters} objects contain the same information. */
161     @Override
equals(Object o)162     public boolean equals(Object o) {
163         if (this == o) return true;
164         if (!(o instanceof AppInstallFilters)) return false;
165         AppInstallFilters that = (AppInstallFilters) o;
166         return mPackageNames.equals(that.mPackageNames);
167     }
168 
169     /** Returns the hash of the {@link AppInstallFilters} object's data. */
170     @Override
hashCode()171     public int hashCode() {
172         return Objects.hash(mPackageNames);
173     }
174 
175     @Override
toString()176     public String toString() {
177         return "AppInstallFilters{" + "mPackageNames=" + mPackageNames + '}';
178     }
179 
180     /** Builder for creating {@link AppInstallFilters} objects. */
181     public static final class Builder {
182         @NonNull private Set<String> mPackageNames = new HashSet<>();
183 
Builder()184         public Builder() {}
185 
186         /**
187          * Gets the list of package names this ad is filtered on.
188          *
189          * <p>See {@link #getPackageNames()} for more information.
190          */
191         @NonNull
setPackageNames(@onNull Set<String> packageNames)192         public Builder setPackageNames(@NonNull Set<String> packageNames) {
193             Objects.requireNonNull(packageNames);
194             mPackageNames = packageNames;
195             return this;
196         }
197 
198         /** Builds and returns a {@link AppInstallFilters} instance. */
199         @NonNull
build()200         public AppInstallFilters build() {
201             return new AppInstallFilters(this);
202         }
203     }
204 }
205