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