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