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