• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 com.android.ondevicepersonalization.services.data.user;
18 
19 import static android.adservices.ondevicepersonalization.Constants.API_NAME_ADSERVICES_GET_COMMON_STATES;
20 import static android.adservices.ondevicepersonalization.Constants.STATUS_CALLER_NOT_ALLOWED;
21 import static android.adservices.ondevicepersonalization.Constants.STATUS_CLASS_NOT_FOUND;
22 import static android.adservices.ondevicepersonalization.Constants.STATUS_EXECUTION_INTERRUPTED;
23 import static android.adservices.ondevicepersonalization.Constants.STATUS_INTERNAL_ERROR;
24 import static android.adservices.ondevicepersonalization.Constants.STATUS_METHOD_NOT_FOUND;
25 import static android.adservices.ondevicepersonalization.Constants.STATUS_NULL_ADSERVICES_COMMON_MANAGER;
26 import static android.adservices.ondevicepersonalization.Constants.STATUS_REMOTE_EXCEPTION;
27 import static android.adservices.ondevicepersonalization.Constants.STATUS_SUCCESS;
28 import static android.adservices.ondevicepersonalization.Constants.STATUS_TIMEOUT;
29 
30 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__API_REMOTE_EXCEPTION;
31 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__ODP;
32 import static com.android.ondevicepersonalization.services.FlagsConstants.KEY_ENABLE_PERSONALIZATION_STATUS_OVERRIDE;
33 import static com.android.ondevicepersonalization.services.FlagsConstants.KEY_PERSONALIZATION_STATUS_OVERRIDE_VALUE;
34 import static com.android.ondevicepersonalization.services.FlagsConstants.KEY_USER_CONTROL_CACHE_IN_MILLIS;
35 
36 import com.android.internal.annotations.VisibleForTesting;
37 import com.android.odp.module.common.Clock;
38 import com.android.odp.module.common.MonotonicClock;
39 import com.android.ondevicepersonalization.internal.util.LoggerFactory;
40 import com.android.ondevicepersonalization.services.OnDevicePersonalizationApplication;
41 import com.android.ondevicepersonalization.services.StableFlags;
42 import com.android.ondevicepersonalization.services.reset.ResetDataJob;
43 import com.android.ondevicepersonalization.services.statsd.errorlogging.ClientErrorLogger;
44 import com.android.ondevicepersonalization.services.util.DebugUtils;
45 import com.android.ondevicepersonalization.services.util.StatsUtils;
46 
47 import java.util.Objects;
48 import java.util.concurrent.ExecutionException;
49 import java.util.concurrent.TimeoutException;
50 
51 /**
52  * A singleton class that stores all user privacy statuses in memory.
53  */
54 public final class UserPrivacyStatus {
55     private static final String TAG = "UserPrivacyStatus";
56     private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
57     private static final String PERSONALIZATION_STATUS_KEY = "PERSONALIZATION_STATUS";
58     @VisibleForTesting
59     static final int CONTROL_GIVEN_STATUS_CODE = 3;
60     @VisibleForTesting
61     static final int CONTROL_REVOKED_STATUS_CODE = 2;
62     private static final Object sLock = new Object();
63     private static volatile UserPrivacyStatus sUserPrivacyStatus = null;
64     private boolean mProtectedAudienceEnabled;
65     private boolean mMeasurementEnabled;
66     private boolean mProtectedAudienceReset;
67     private boolean mMeasurementReset;
68     private long mLastUserControlCacheUpdate;
69     private final Clock mClock;
70     private final AdServicesCommonStatesWrapper mAdServicesCommonStatesWrapper;
71 
72     @VisibleForTesting
UserPrivacyStatus( AdServicesCommonStatesWrapper wrapper, Clock clock)73     UserPrivacyStatus(
74             AdServicesCommonStatesWrapper wrapper,
75             Clock clock) {
76         // Assume the more privacy-safe option until updated.
77         mProtectedAudienceEnabled = false;
78         mMeasurementEnabled = false;
79         mProtectedAudienceReset = false;
80         mMeasurementReset = false;
81         mLastUserControlCacheUpdate = -1L;
82         mAdServicesCommonStatesWrapper = Objects.requireNonNull(wrapper);
83         mClock = Objects.requireNonNull(clock);
84     }
85 
86     /** Returns an instance of UserPrivacyStatus. */
getInstance()87     public static UserPrivacyStatus getInstance() {
88         if (sUserPrivacyStatus == null) {
89             synchronized (sLock) {
90                 if (sUserPrivacyStatus == null) {
91                     sUserPrivacyStatus = new UserPrivacyStatus(
92                             new AdServicesCommonStatesWrapperImpl(
93                                     OnDevicePersonalizationApplication.getAppContext()),
94                             MonotonicClock.getInstance());
95                 }
96             }
97         }
98         return sUserPrivacyStatus;
99     }
100 
isOverrideEnabled()101     private static boolean isOverrideEnabled() {
102         return DebugUtils.isDeveloperModeEnabled(
103                 OnDevicePersonalizationApplication.getAppContext())
104                 && (boolean) StableFlags.get(KEY_ENABLE_PERSONALIZATION_STATUS_OVERRIDE);
105     }
106 
107     /**
108      * Return if both Protected Audience (PA) and Measurement consent status are disabled
109      */
isProtectedAudienceAndMeasurementBothDisabled()110     public boolean isProtectedAudienceAndMeasurementBothDisabled() {
111         if (isOverrideEnabled()) {
112             boolean overrideToBothEnabled =
113                     (boolean) StableFlags.get(KEY_PERSONALIZATION_STATUS_OVERRIDE_VALUE);
114             return !overrideToBothEnabled;
115         }
116         if (isUserControlCacheValid()) {
117             return !mProtectedAudienceEnabled && !mMeasurementEnabled;
118         }
119         // make request to AdServices#getCommonStates API once
120         fetchStateFromAdServices();
121         return !mProtectedAudienceEnabled && !mMeasurementEnabled;
122     }
123 
124     /**
125      * Returns the user control status of Protected Audience (PA).
126      */
isProtectedAudienceEnabled()127     public boolean isProtectedAudienceEnabled() {
128         if (isOverrideEnabled()) {
129             return (boolean) StableFlags.get(KEY_PERSONALIZATION_STATUS_OVERRIDE_VALUE);
130         }
131         if (isUserControlCacheValid()) {
132             return mProtectedAudienceEnabled;
133         }
134         // make request to AdServices#getCommonStates API.
135         fetchStateFromAdServices();
136         return mProtectedAudienceEnabled;
137     }
138 
139     /**
140      * Returns the user control status of Measurement.
141      */
isMeasurementEnabled()142     public boolean isMeasurementEnabled() {
143         if (isOverrideEnabled()) {
144             return (boolean) StableFlags.get(KEY_PERSONALIZATION_STATUS_OVERRIDE_VALUE);
145         }
146         if (isUserControlCacheValid()) {
147             return mMeasurementEnabled;
148         }
149         // make request to AdServices#getCommonStates API.
150         fetchStateFromAdServices();
151         return mMeasurementEnabled;
152     }
153 
154     /**
155      * Returns true if the user requests a reset on PA-related data.
156      */
isProtectedAudienceReset()157     private boolean isProtectedAudienceReset() {
158         return mProtectedAudienceReset;
159     }
160 
161     /**
162      * Returns true if the user requests a reset on measurement-related data.
163      */
isMeasurementReset()164     private boolean isMeasurementReset() {
165         return mMeasurementReset;
166     }
167 
168     /**
169      * Update user control cache and timestamp metadata.
170      */
171     @VisibleForTesting
updateUserControlCache(int protectedAudienceState, int measurementState)172     void updateUserControlCache(int protectedAudienceState,
173             int measurementState) {
174         mProtectedAudienceEnabled = (protectedAudienceState != CONTROL_REVOKED_STATUS_CODE);
175         mMeasurementEnabled = (measurementState != CONTROL_REVOKED_STATUS_CODE);
176         mProtectedAudienceReset = (protectedAudienceState != CONTROL_GIVEN_STATUS_CODE);
177         mMeasurementReset = (measurementState != CONTROL_GIVEN_STATUS_CODE);
178         mLastUserControlCacheUpdate = mClock.currentTimeMillis();
179         handleResetIfNeeded();
180     }
181 
182     /**
183      * Returns whether the user control cache remains valid.
184      */
185     @VisibleForTesting
isUserControlCacheValid()186     boolean isUserControlCacheValid() {
187         if (mLastUserControlCacheUpdate == -1L) {
188             return false;
189         }
190         long cacheDuration = mClock.currentTimeMillis() - mLastUserControlCacheUpdate;
191         return cacheDuration >= 0
192                 && cacheDuration < (long) StableFlags.get(KEY_USER_CONTROL_CACHE_IN_MILLIS);
193     }
194 
195     /**
196      * Reset user control info for testing.
197      */
198     @VisibleForTesting
resetUserControlForTesting()199     void resetUserControlForTesting() {
200         mProtectedAudienceEnabled = false;
201         mMeasurementEnabled = false;
202         mProtectedAudienceReset = false;
203         mMeasurementReset = false;
204         mLastUserControlCacheUpdate = -1L;
205     }
206 
207     /**
208      * Invalidate the user control cache for testing.
209      */
210     @VisibleForTesting
invalidateUserControlCacheForTesting()211     void invalidateUserControlCacheForTesting() {
212         mLastUserControlCacheUpdate = mClock.currentTimeMillis()
213                         - 2 * (long) StableFlags.get(KEY_USER_CONTROL_CACHE_IN_MILLIS);
214     }
215 
fetchStateFromAdServices()216     private void fetchStateFromAdServices() {
217         long startTime = mClock.elapsedRealtime();
218         String packageName = OnDevicePersonalizationApplication.getAppContext().getPackageName();
219         try {
220             // IPC.
221             AdServicesCommonStatesWrapper.CommonStatesResult commonStates =
222                     mAdServicesCommonStatesWrapper.getCommonStates().get();
223             StatsUtils.writeServiceRequestMetrics(
224                     API_NAME_ADSERVICES_GET_COMMON_STATES,
225                     packageName,
226                     null,
227                     mClock,
228                     STATUS_SUCCESS,
229                     startTime);
230             // update cache.
231             int updatedProtectedAudienceState = commonStates.getPaState();
232             int updatedMeasurementState = commonStates.getMeasurementState();
233             updateUserControlCache(updatedProtectedAudienceState, updatedMeasurementState);
234         } catch (Exception e) {
235             int statusCode = getExceptionStatus(e);
236             sLogger.e(e, TAG + ": fetchStateFromAdServices error, status code %d", statusCode);
237             StatsUtils.writeServiceRequestMetrics(
238                     API_NAME_ADSERVICES_GET_COMMON_STATES,
239                     packageName,
240                     null,
241                     mClock,
242                     statusCode,
243                     startTime);
244             ClientErrorLogger.getInstance()
245                     .logError(
246                             e,
247                             AD_SERVICES_ERROR_REPORTED__ERROR_CODE__API_REMOTE_EXCEPTION,
248                             AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__ODP);
249         }
250     }
251 
handleResetIfNeeded()252     private void handleResetIfNeeded() {
253         if (isMeasurementReset() || isProtectedAudienceReset()) {
254             ResetDataJob.schedule(OnDevicePersonalizationApplication.getAppContext());
255         }
256     }
257 
258     @VisibleForTesting
getExceptionStatus(Exception e)259     int getExceptionStatus(Exception e) {
260         if (e instanceof InterruptedException) {
261             return STATUS_EXECUTION_INTERRUPTED;
262         }
263 
264         Throwable cause = e;
265         if (e instanceof ExecutionException) {
266             cause = e.getCause(); // Unwrap the cause
267         }
268         if (cause instanceof TimeoutException) {
269             return STATUS_TIMEOUT;
270         }
271         if (cause instanceof NoSuchMethodException) {
272             return STATUS_METHOD_NOT_FOUND;
273         }
274         if (cause instanceof SecurityException) {
275             return STATUS_CALLER_NOT_ALLOWED;
276         }
277         if (cause instanceof IllegalStateException) {
278             return STATUS_INTERNAL_ERROR;
279         }
280         if (cause instanceof IllegalArgumentException) {
281             return STATUS_INTERNAL_ERROR;
282         }
283         if (cause instanceof NoClassDefFoundError) {
284             return STATUS_CLASS_NOT_FOUND;
285         }
286         if (cause instanceof AdServicesCommonStatesWrapper.NullAdServiceCommonManagerException) {
287             return STATUS_NULL_ADSERVICES_COMMON_MANAGER;
288         }
289         if (cause instanceof InterruptedException) {
290             return STATUS_EXECUTION_INTERRUPTED;
291         }
292         return STATUS_REMOTE_EXCEPTION;
293     }
294 }
295