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 com.android.ondevicepersonalization.services.PhFlags.KEY_ENABLE_PERSONALIZATION_STATUS_OVERRIDE; 20 import static com.android.ondevicepersonalization.services.PhFlags.KEY_PERSONALIZATION_STATUS_OVERRIDE_VALUE; 21 import static com.android.ondevicepersonalization.services.PhFlags.KEY_USER_CONTROL_CACHE_IN_MILLIS; 22 23 import android.adservices.common.AdServicesCommonManager; 24 import android.adservices.common.AdServicesCommonStates; 25 import android.adservices.common.AdServicesCommonStatesResponse; 26 import android.adservices.common.AdServicesOutcomeReceiver; 27 import android.adservices.ondevicepersonalization.Constants; 28 import android.annotation.NonNull; 29 import android.content.Context; 30 import android.ondevicepersonalization.IOnDevicePersonalizationSystemService; 31 import android.ondevicepersonalization.IOnDevicePersonalizationSystemServiceCallback; 32 import android.ondevicepersonalization.OnDevicePersonalizationSystemServiceManager; 33 import android.os.Bundle; 34 35 import androidx.concurrent.futures.CallbackToFutureAdapter; 36 37 import com.android.internal.annotations.VisibleForTesting; 38 import com.android.modules.utils.build.SdkLevel; 39 import com.android.odp.module.common.Clock; 40 import com.android.odp.module.common.MonotonicClock; 41 import com.android.ondevicepersonalization.internal.util.LoggerFactory; 42 import com.android.ondevicepersonalization.services.Flags; 43 import com.android.ondevicepersonalization.services.FlagsFactory; 44 import com.android.ondevicepersonalization.services.OnDevicePersonalizationApplication; 45 import com.android.ondevicepersonalization.services.OnDevicePersonalizationExecutors; 46 import com.android.ondevicepersonalization.services.reset.ResetDataJobService; 47 import com.android.ondevicepersonalization.services.util.DebugUtils; 48 49 50 import com.google.common.util.concurrent.ListenableFuture; 51 52 /** 53 * A singleton class that stores all user privacy statuses in memory. 54 */ 55 public final class UserPrivacyStatus { 56 private static final String TAG = "UserPrivacyStatus"; 57 private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger(); 58 private static final Clock sClock = MonotonicClock.getInstance(); 59 private static final String PERSONALIZATION_STATUS_KEY = "PERSONALIZATION_STATUS"; 60 @VisibleForTesting 61 static final int CONTROL_GIVEN_STATUS_CODE = 3; 62 @VisibleForTesting 63 static final int CONTROL_REVOKED_STATUS_CODE = 2; 64 static volatile UserPrivacyStatus sUserPrivacyStatus = null; 65 private boolean mPersonalizationStatusEnabled; 66 private boolean mProtectedAudienceEnabled; 67 private boolean mMeasurementEnabled; 68 private boolean mProtectedAudienceReset; 69 private boolean mMeasurementReset; 70 private long mLastUserControlCacheUpdate; 71 UserPrivacyStatus()72 private UserPrivacyStatus() { 73 // Assume the more privacy-safe option until updated. 74 mPersonalizationStatusEnabled = false; 75 mProtectedAudienceEnabled = false; 76 mMeasurementEnabled = false; 77 mProtectedAudienceReset = false; 78 mMeasurementReset = false; 79 mLastUserControlCacheUpdate = -1L; 80 } 81 82 /** Returns an instance of UserPrivacyStatus. */ getInstance()83 public static UserPrivacyStatus getInstance() { 84 if (sUserPrivacyStatus == null) { 85 synchronized (UserPrivacyStatus.class) { 86 if (sUserPrivacyStatus == null) { 87 sUserPrivacyStatus = new UserPrivacyStatus(); 88 // Restore personalization status from the system server on U+ devices. 89 if (SdkLevel.isAtLeastU()) { 90 sUserPrivacyStatus.restorePersonalizationStatus(); 91 } 92 } 93 } 94 } 95 return sUserPrivacyStatus; 96 } 97 98 /** Returns an instance of UserPrivacyStatus. */ 99 @VisibleForTesting getInstanceForTest()100 public static UserPrivacyStatus getInstanceForTest() { 101 if (sUserPrivacyStatus == null) { 102 synchronized (UserPrivacyStatus.class) { 103 if (sUserPrivacyStatus == null) { 104 sUserPrivacyStatus = new UserPrivacyStatus(); 105 } 106 } 107 } 108 return sUserPrivacyStatus; 109 } 110 isOverrideEnabled()111 private static boolean isOverrideEnabled() { 112 Flags flags = FlagsFactory.getFlags(); 113 return DebugUtils.isDeveloperModeEnabled( 114 OnDevicePersonalizationApplication.getAppContext()) 115 && (boolean) flags.getStableFlag(KEY_ENABLE_PERSONALIZATION_STATUS_OVERRIDE); 116 } 117 setPersonalizationStatusEnabled(boolean personalizationStatusEnabled)118 public void setPersonalizationStatusEnabled(boolean personalizationStatusEnabled) { 119 Flags flags = FlagsFactory.getFlags(); 120 if (!isOverrideEnabled()) { 121 mPersonalizationStatusEnabled = personalizationStatusEnabled; 122 } 123 } 124 isPersonalizationStatusEnabled()125 public boolean isPersonalizationStatusEnabled() { 126 Flags flags = FlagsFactory.getFlags(); 127 if (isOverrideEnabled()) { 128 return (boolean) flags.getStableFlag(KEY_PERSONALIZATION_STATUS_OVERRIDE_VALUE); 129 } 130 return mPersonalizationStatusEnabled; 131 } 132 133 /** 134 * Returns the user control status of Protected Audience (PA). 135 */ isProtectedAudienceEnabled()136 public boolean isProtectedAudienceEnabled() { 137 Flags flags = FlagsFactory.getFlags(); 138 if (isOverrideEnabled()) { 139 return (boolean) flags.getStableFlag(KEY_PERSONALIZATION_STATUS_OVERRIDE_VALUE); 140 } 141 if (isUserControlCacheValid()) { 142 return mProtectedAudienceEnabled; 143 } 144 // make request to AdServices#getCommonStates API. 145 fetchStateFromAdServices(); 146 return mProtectedAudienceEnabled; 147 } 148 149 /** 150 * Returns the user control status of Measurement. 151 */ isMeasurementEnabled()152 public boolean isMeasurementEnabled() { 153 Flags flags = FlagsFactory.getFlags(); 154 if (isOverrideEnabled()) { 155 return (boolean) flags.getStableFlag(KEY_PERSONALIZATION_STATUS_OVERRIDE_VALUE); 156 } 157 if (isUserControlCacheValid()) { 158 return mMeasurementEnabled; 159 } 160 // make request to AdServices#getCommonStates API. 161 fetchStateFromAdServices(); 162 return mMeasurementEnabled; 163 } 164 165 /** 166 * Returns true if the user requests a reset on PA-related data. 167 */ isProtectedAudienceReset()168 private boolean isProtectedAudienceReset() { 169 return mProtectedAudienceReset; 170 } 171 172 /** 173 * Returns true if the user requests a reset on measurement-related data. 174 */ isMeasurementReset()175 private boolean isMeasurementReset() { 176 return mMeasurementReset; 177 } 178 179 /** 180 * Update user control cache and timestamp metadata. 181 */ 182 @VisibleForTesting updateUserControlCache(int protectedAudienceState, int measurementState)183 void updateUserControlCache(int protectedAudienceState, 184 int measurementState) { 185 mProtectedAudienceEnabled = (protectedAudienceState != CONTROL_REVOKED_STATUS_CODE); 186 mMeasurementEnabled = (measurementState != CONTROL_REVOKED_STATUS_CODE); 187 mProtectedAudienceReset = (protectedAudienceState != CONTROL_GIVEN_STATUS_CODE); 188 mMeasurementReset = (measurementState != CONTROL_GIVEN_STATUS_CODE); 189 mLastUserControlCacheUpdate = sClock.currentTimeMillis(); 190 handleResetIfNeeded(); 191 } 192 193 /** 194 * Returns whether the user control cache remains valid. 195 */ 196 @VisibleForTesting isUserControlCacheValid()197 boolean isUserControlCacheValid() { 198 if (mLastUserControlCacheUpdate == -1L) { 199 return false; 200 } 201 long cacheDuration = sClock.currentTimeMillis() - mLastUserControlCacheUpdate; 202 return cacheDuration >= 0 203 && cacheDuration < (long) FlagsFactory.getFlags().getStableFlag( 204 KEY_USER_CONTROL_CACHE_IN_MILLIS); 205 } 206 207 /** 208 * Reset user control info for testing. 209 */ 210 @VisibleForTesting resetUserControlForTesting()211 void resetUserControlForTesting() { 212 mProtectedAudienceEnabled = false; 213 mMeasurementEnabled = false; 214 mProtectedAudienceReset = false; 215 mMeasurementReset = false; 216 mLastUserControlCacheUpdate = -1L; 217 } 218 219 /** 220 * Invalidate the user control cache for testing. 221 */ 222 @VisibleForTesting invalidateUserControlCacheForTesting()223 void invalidateUserControlCacheForTesting() { 224 mLastUserControlCacheUpdate = sClock.currentTimeMillis() 225 - 2 * (long) FlagsFactory.getFlags().getStableFlag( 226 KEY_USER_CONTROL_CACHE_IN_MILLIS); 227 } 228 fetchStateFromAdServices()229 private void fetchStateFromAdServices() { 230 try { 231 // IPC. 232 AdServicesCommonManager adServicesCommonManager = getAdServicesCommonManager(); 233 AdServicesCommonStates commonStates = 234 getAdServicesCommonStates(adServicesCommonManager); 235 236 // update cache. 237 int updatedProtectedAudienceState = commonStates.getPaState(); 238 int updatedMeasurementState = commonStates.getMeasurementState(); 239 updateUserControlCache(updatedProtectedAudienceState, updatedMeasurementState); 240 } catch (Exception e) { 241 sLogger.e(TAG + ": fetchStateFromAdServices error", e); 242 } 243 } 244 handleResetIfNeeded()245 private void handleResetIfNeeded() { 246 if (isMeasurementReset() || isProtectedAudienceReset()) { 247 ResetDataJobService.schedule(); 248 } 249 } 250 251 /** 252 * Get AdServices common manager from ODP. 253 */ getAdServicesCommonManager()254 private static AdServicesCommonManager getAdServicesCommonManager() { 255 Context odpContext = OnDevicePersonalizationApplication.getAppContext(); 256 try { 257 return odpContext.getSystemService(AdServicesCommonManager.class); 258 } catch (NoClassDefFoundError e) { 259 throw new IllegalStateException("Cannot find AdServicesCommonManager.", e); 260 } 261 } 262 263 /** 264 * Get common states from AdServices, such as user control. 265 */ getAdServicesCommonStates( @onNull AdServicesCommonManager adServicesCommonManager)266 private AdServicesCommonStates getAdServicesCommonStates( 267 @NonNull AdServicesCommonManager adServicesCommonManager) { 268 ListenableFuture<AdServicesCommonStatesResponse> response = 269 getAdServicesResponse(adServicesCommonManager); 270 try { 271 return response.get().getAdServicesCommonStates(); 272 } catch (Exception e) { 273 throw new IllegalStateException("Failed when calling " 274 + "AdServicesCommonManager#getAdServicesCommonStates().", e); 275 } 276 } 277 278 /** 279 * IPC to AdServices API. 280 */ getAdServicesResponse( @onNull AdServicesCommonManager adServicesCommonManager)281 private ListenableFuture<AdServicesCommonStatesResponse> getAdServicesResponse( 282 @NonNull AdServicesCommonManager adServicesCommonManager) { 283 return CallbackToFutureAdapter.getFuture( 284 completer -> { 285 adServicesCommonManager.getAdservicesCommonStates( 286 OnDevicePersonalizationExecutors.getBackgroundExecutor(), 287 new AdServicesOutcomeReceiver<AdServicesCommonStatesResponse, 288 Exception>() { 289 @Override 290 public void onResult(AdServicesCommonStatesResponse result) { 291 completer.set(result); 292 } 293 294 @Override 295 public void onError(Exception error) { 296 completer.setException(error); 297 } 298 }); 299 // For debugging purpose only. 300 return "getAdServicesCommonStates"; 301 } 302 ); 303 } 304 305 // TODO (b/331684191): remove SecurityException after mocking all UserPrivacyStatus 306 private void restorePersonalizationStatus() { 307 if (isOverrideEnabled()) { 308 return; 309 } 310 Context odpContext = OnDevicePersonalizationApplication.getAppContext(); 311 OnDevicePersonalizationSystemServiceManager systemServiceManager = 312 odpContext.getSystemService(OnDevicePersonalizationSystemServiceManager.class); 313 if (systemServiceManager != null) { 314 IOnDevicePersonalizationSystemService systemService = 315 systemServiceManager.getService(); 316 if (systemService != null) { 317 try { 318 systemService.readPersonalizationStatus( 319 new IOnDevicePersonalizationSystemServiceCallback.Stub() { 320 @Override 321 public void onResult(Bundle bundle) { 322 boolean personalizationStatus = 323 bundle.getBoolean(PERSONALIZATION_STATUS_KEY); 324 setPersonalizationStatusEnabled(personalizationStatus); 325 } 326 327 @Override 328 public void onError(int errorCode) { 329 if (errorCode == Constants.STATUS_KEY_NOT_FOUND) { 330 sLogger.d( 331 TAG 332 + ": Personalization status " 333 + "not found in the system server"); 334 } 335 } 336 }); 337 } catch (Exception e) { 338 sLogger.e(TAG + ": Error when reading personalization status.", e); 339 } 340 } else { 341 sLogger.w(TAG + ": System service is not ready."); 342 } 343 } else { 344 sLogger.w(TAG + ": Cannot find system server on U+ devices."); 345 } 346 } 347 } 348