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