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