1 /* 2 * Copyright 2024 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 androidx.privacysandbox.ads.adservices.java.signals 18 19 import android.adservices.common.AdServicesPermissions 20 import android.content.Context 21 import androidx.annotation.DoNotInline 22 import androidx.annotation.RequiresPermission 23 import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures 24 import androidx.privacysandbox.ads.adservices.java.internal.asListenableFuture 25 import androidx.privacysandbox.ads.adservices.signals.ProtectedSignalsManager 26 import androidx.privacysandbox.ads.adservices.signals.ProtectedSignalsManager.Companion.obtain 27 import androidx.privacysandbox.ads.adservices.signals.UpdateSignalsRequest 28 import com.google.common.util.concurrent.ListenableFuture 29 import kotlinx.coroutines.CoroutineScope 30 import kotlinx.coroutines.Dispatchers 31 import kotlinx.coroutines.async 32 33 /** 34 * This class provides APIs for app and ad-SDKs to interact with protected signals. This class can 35 * be used by Java clients. 36 */ 37 @OptIn(ExperimentalFeatures.Ext12OptIn::class) 38 abstract class ProtectedSignalsManagerFutures internal constructor() { 39 40 /** 41 * The updateSignals API will retrieve a JSON from the URI that describes which signals to add 42 * or remove. This API also allows registering the encoder endpoint. The endpoint is used to 43 * download an encoding logic, which enables encoding the signals. 44 * 45 * <p>The top level keys for the JSON must correspond to one of 5 commands: 46 * 47 * <p>"put" - Adds a new signal, overwriting any existing signals with the same key. The value 48 * for this is a JSON object where the keys are base 64 strings corresponding to the key to put 49 * for and the values are base 64 string corresponding to the value to put. 50 * 51 * <p>"append" - Appends a new signal/signals to a time series of signals, removing the oldest 52 * signals to make room for the new ones if the size of the series exceeds the given maximum. 53 * The value for this is a JSON object where the keys are base 64 strings corresponding to the 54 * key to append to and the values are objects with two fields: "values" and "maxSignals" . 55 * "values" is a list of base 64 strings corresponding to signal values to append to the time 56 * series. "maxSignals" is the maximum number of values that are allowed in this timeseries. If 57 * the current number of signals associated with the key exceeds maxSignals the oldest signals 58 * will be removed. Note that you can append to a key added by put. Not that appending more than 59 * the maximum number of values will cause a failure. 60 * 61 * <p>"put_if_not_present" - Adds a new signal only if there are no existing signals with the 62 * same key. The value for this is a JSON object where the keys are base 64 strings 63 * corresponding to the key to put for and the values are base 64 string corresponding to the 64 * value to put. 65 * 66 * <p>"remove" - Removes the signal for a key. The value of this is a list of base 64 strings 67 * corresponding to the keys of signals that should be deleted. 68 * 69 * <p>"update_encoder" - Provides an action to update the endpoint, and a URI which can be used 70 * to retrieve an encoding logic. The sub-key for providing an update action is "action" and the 71 * values currently supported are: 72 * <ol> 73 * <li>"REGISTER" : Registers the encoder endpoint if provided for the first time or overwrites 74 * the existing one with the newly provided endpoint. Providing the "endpoint" is required for 75 * the "REGISTER" action. 76 * </ol> 77 * 78 * <p>The sub-key for providing an encoder endpoint is "endpoint" and the value is the URI 79 * string for the endpoint. 80 * 81 * <p>Key may only be operated on by one command per JSON. If two command attempt to operate on 82 * the same key, this method will through an {@link IllegalArgumentException} 83 * 84 * <p>This call fails with an {@link SecurityException} if 85 * <ol> 86 * <li>the {@code ownerPackageName} is not calling app's package name and/or 87 * <li>the buyer is not authorized to use the API. 88 * </ol> 89 * 90 * <p>This call fails with an {@link IllegalArgumentException} if 91 * <ol> 92 * <li>The JSON retrieved from the server is not valid. 93 * <li>The provided URI is invalid. 94 * </ol> 95 * 96 * <p>This call fails with {@link LimitExceededException} if the calling package exceeds the 97 * allowed rate limits and is throttled. 98 * 99 * <p>This call fails with an {@link IllegalStateException} if an internal service error is 100 * encountered. 101 */ 102 @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_PROTECTED_SIGNALS) updateSignalsAsyncnull103 abstract fun updateSignalsAsync(request: UpdateSignalsRequest): ListenableFuture<Unit> 104 105 private class JavaImpl(private val mProtectedSignalsManager: ProtectedSignalsManager?) : 106 ProtectedSignalsManagerFutures() { 107 @DoNotInline 108 @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_PROTECTED_SIGNALS) 109 override fun updateSignalsAsync(request: UpdateSignalsRequest): ListenableFuture<Unit> { 110 return CoroutineScope(Dispatchers.Default) 111 .async { mProtectedSignalsManager!!.updateSignals(request) } 112 .asListenableFuture() 113 } 114 } 115 116 companion object { 117 /** 118 * Creates [ProtectedSignalsManagerFutures]. 119 * 120 * @return ProtectedSignalsManagerFutures object. If the device is running an incompatible 121 * build (adservices extension version < 12), the value returned is null. 122 */ 123 @JvmStatic fromnull124 fun from(context: Context): ProtectedSignalsManagerFutures? { 125 return obtain(context)?.let { JavaImpl(it) } 126 } 127 } 128 } 129