• 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.adservices.ui.notifications;
18 
19 import static android.adservices.common.AdServicesCommonManager.ACTION_ADSERVICES_NOTIFICATION_DISPLAYED;
20 import static android.adservices.common.AdServicesCommonManager.ACTION_VIEW_ADSERVICES_CONSENT_PAGE;
21 import static android.adservices.common.AdServicesPermissions.MODIFY_ADSERVICES_STATE;
22 import static android.adservices.common.AdServicesPermissions.MODIFY_ADSERVICES_STATE_COMPAT;
23 
24 import static com.android.adservices.service.FlagsConstants.KEY_GA_UX_FEATURE_ENABLED;
25 import static com.android.adservices.service.FlagsConstants.KEY_PAS_UX_ENABLED;
26 import static com.android.adservices.service.FlagsConstants.KEY_RECORD_MANUAL_INTERACTION_ENABLED;
27 import static com.android.adservices.service.FlagsConstants.KEY_UI_OTA_RESOURCES_FEATURE_ENABLED;
28 import static com.android.adservices.service.FlagsConstants.KEY_UI_OTA_STRINGS_FEATURE_ENABLED;
29 import static com.android.adservices.service.consent.ConsentManager.MANUAL_INTERACTIONS_RECORDED;
30 import static com.android.adservices.service.consent.ConsentManager.NO_MANUAL_INTERACTIONS_RECORDED;
31 import static com.android.adservices.ui.UxUtil.isUxStatesReady;
32 import static com.android.adservices.ui.ganotifications.ConsentNotificationPasFragment.IS_RENOTIFY_KEY;
33 import static com.android.adservices.ui.ganotifications.ConsentNotificationPasFragment.IS_STRICT_CONSENT_BEHAVIOR;
34 
35 import android.app.Notification;
36 import android.app.NotificationChannel;
37 import android.app.NotificationManager;
38 import android.app.PendingIntent;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.pm.PackageManager;
42 import android.content.pm.ResolveInfo;
43 import android.os.Build;
44 
45 import androidx.annotation.NonNull;
46 import androidx.annotation.RequiresApi;
47 import androidx.core.app.NotificationCompat;
48 import androidx.core.app.NotificationManagerCompat;
49 
50 import com.android.adservices.LogUtil;
51 import com.android.adservices.api.R;
52 import com.android.adservices.service.FlagsFactory;
53 import com.android.adservices.service.common.PermissionHelper;
54 import com.android.adservices.service.consent.AdServicesApiType;
55 import com.android.adservices.service.consent.ConsentManager;
56 import com.android.adservices.service.stats.UiStatsLogger;
57 import com.android.adservices.service.ui.data.UxStatesManager;
58 import com.android.adservices.ui.OTAResourcesManager;
59 import com.android.adservices.ui.UxUtil;
60 
61 import java.util.List;
62 
63 /** Provides methods which can be used to display Privacy Sandbox consent notification. */
64 @RequiresApi(Build.VERSION_CODES.S)
65 public class ConsentNotificationTrigger {
66 
67     /* Random integer for NotificationCompat purposes. */
68     public static final int NOTIFICATION_ID = 67920;
69     private static final String CHANNEL_ID = "PRIVACY_SANDBOX_CHANNEL";
70     private static final int NOTIFICATION_PRIORITY = NotificationCompat.PRIORITY_MAX;
71     // Request codes should be different for notifications that are not mutually exclusive per user.
72     // When in doubt, always use a different request code.
73     private static final int BETA_REQUEST_CODE = 1;
74     private static final int GA_REQUEST_CODE = 2;
75     private static final int U18_REQUEST_CODE = 3;
76     private static final int GA_WITH_PAS_REQUEST_CODE = 4;
77     private static final int NON_PERSONALIZED_REQUEST_CODE = 5;
78     private static final int PERSONALIZED_FIRST_TIME_REQUEST_CODE = 6;
79     private static final int PERSONALIZED_RENOTIFY_REQUEST_CODE = 7;
80 
81     /**
82      * Shows consent notification as the highest priority notification to the user. Differs from V1
83      * as no business logic is called, and an outside app must handle the corresponding intents for
84      * notification page content and consent changes.
85      *
86      * @param context Context which is used to display {@link NotificationCompat}.
87      * @param isRenotify is the notification re-notifying the user.
88      * @param isNewAdPersonalizationModuleEnabled is a personalization API being enabled.
89      * @param isOngoingNotification is the notification supposed to be persistent/ongoing.
90      */
91     @SuppressWarnings("AvoidStaticContext")
showConsentNotificationV2( @onNull Context context, boolean isRenotify, boolean isNewAdPersonalizationModuleEnabled, boolean isOngoingNotification)92     public static void showConsentNotificationV2(
93             @NonNull Context context,
94             boolean isRenotify,
95             boolean isNewAdPersonalizationModuleEnabled,
96             boolean isOngoingNotification) {
97         LogUtil.d("Started requesting notification.");
98         UiStatsLogger.logRequestedNotification();
99 
100         NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
101         ConsentManager consentManager = ConsentManager.getInstance();
102         if (!notificationManager.areNotificationsEnabled()) {
103             LogUtil.d("Notification is disabled.");
104             recordNotificationDisplayed(context, true, consentManager);
105             UiStatsLogger.logNotificationDisabled();
106             return;
107         }
108 
109         // Set OTA resources if it exists.
110         if (UxStatesManager.getInstance().getFlag(KEY_UI_OTA_STRINGS_FEATURE_ENABLED)
111                 || UxStatesManager.getInstance().getFlag(KEY_UI_OTA_RESOURCES_FEATURE_ENABLED)) {
112             OTAResourcesManager.applyOTAResources(context.getApplicationContext(), true);
113         }
114 
115         createNotificationChannel(context);
116         Notification notification =
117                 getConsentNotificationV2(
118                         context,
119                         isRenotify,
120                         isNewAdPersonalizationModuleEnabled,
121                         isOngoingNotification);
122 
123         notificationManager.notify(NOTIFICATION_ID, notification);
124         LogUtil.d("Sending broadcast about notification being displayed.");
125         sendNotificationBroadcastIntent(context);
126         // TODO(b/378683120):Notification still recorded as displayed to show entry point.
127         //  Change to a unified displayed bit or show entry point bit.
128         recordNotificationDisplayed(context, true, consentManager);
129         consentManager.alignUserChoicesIfNeeded();
130         UiStatsLogger.logNotificationDisplayed();
131         LogUtil.d("Notification was displayed.");
132     }
133 
getConsentNotificationV2( @onNull Context context, boolean isRenotify, boolean isNewAdPersonalizationModuleEnabled, boolean isOngoingNotification)134     private static Notification getConsentNotificationV2(
135             @NonNull Context context,
136             boolean isRenotify,
137             boolean isNewAdPersonalizationModuleEnabled,
138             boolean isOngoingNotification) {
139 
140         Intent intent = getNotificationV2Intent(context);
141         int requestCode;
142         String contentTitle;
143         String contentText;
144         // TODO(b/380065660): Rename resources to exclude business logic
145         if (isNewAdPersonalizationModuleEnabled) {
146             if (isRenotify) {
147                 requestCode = PERSONALIZED_RENOTIFY_REQUEST_CODE;
148                 contentTitle = context.getString(R.string.notificationUI_pas_re_notification_title);
149                 contentText =
150                         context.getString(R.string.notificationUI_pas_re_notification_content);
151             } else {
152                 requestCode = PERSONALIZED_FIRST_TIME_REQUEST_CODE;
153                 contentTitle = context.getString(R.string.notificationUI_pas_notification_title);
154                 contentText = context.getString(R.string.notificationUI_pas_notification_content);
155             }
156         } else {
157             requestCode = NON_PERSONALIZED_REQUEST_CODE;
158             contentTitle = context.getString(R.string.notificationUI_u18_notification_title);
159             contentText = context.getString(R.string.notificationUI_u18_notification_content);
160         }
161 
162         intent.putExtra(IS_RENOTIFY_KEY, isRenotify);
163 
164         PendingIntent pendingIntent =
165                 PendingIntent.getActivity(
166                         context, requestCode, intent, PendingIntent.FLAG_IMMUTABLE);
167 
168         NotificationCompat.BigTextStyle textStyle =
169                 new NotificationCompat.BigTextStyle().bigText(contentText);
170 
171         NotificationCompat.Builder notificationBuilder =
172                 new NotificationCompat.Builder(context, CHANNEL_ID)
173                         .setSmallIcon(R.drawable.ic_info_icon)
174                         .setContentTitle(contentTitle)
175                         .setContentText(contentText)
176                         .setStyle(textStyle)
177                         .setPriority(NOTIFICATION_PRIORITY)
178                         .setAutoCancel(true)
179                         .setContentIntent(pendingIntent);
180         Notification notification = notificationBuilder.build();
181         if (isOngoingNotification) {
182             notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
183         }
184         return notification;
185     }
186 
getNotificationV2Intent(Context context)187     private static Intent getNotificationV2Intent(Context context) {
188         Intent intent = new Intent(ACTION_VIEW_ADSERVICES_CONSENT_PAGE);
189         PackageManager pm = context.getPackageManager();
190         List<ResolveInfo> activities = pm.queryIntentActivities(intent, 0);
191         for (ResolveInfo resolveInfo : activities) {
192             String packageName = resolveInfo.activityInfo.packageName;
193             if (PermissionHelper.hasPermission(context, packageName, MODIFY_ADSERVICES_STATE)
194                     || PermissionHelper.hasPermission(
195                             context, packageName, MODIFY_ADSERVICES_STATE_COMPAT)) {
196                 LogUtil.d(
197                         "Matching Activity found for %s action in : %s",
198                         ACTION_VIEW_ADSERVICES_CONSENT_PAGE, packageName);
199                 return intent.setPackage(packageName);
200             }
201             LogUtil.w(
202                     "Matching Activity found for %s action in : %s, but it has neither %s nor"
203                             + " %s permission",
204                     ACTION_VIEW_ADSERVICES_CONSENT_PAGE,
205                     packageName,
206                     MODIFY_ADSERVICES_STATE,
207                     MODIFY_ADSERVICES_STATE_COMPAT);
208         }
209         LogUtil.w(
210                 "No activity available in system image to handle "
211                         + "%s action. Falling back to "
212                         + "ConsentNotificationActivity",
213                 ACTION_VIEW_ADSERVICES_CONSENT_PAGE);
214         return new Intent(context, ConsentNotificationActivity.class);
215     }
216 
sendNotificationBroadcastIntent(Context context)217     private static void sendNotificationBroadcastIntent(Context context) {
218         Intent intent = new Intent(ACTION_ADSERVICES_NOTIFICATION_DISPLAYED);
219         PackageManager pm = context.getPackageManager();
220         List<ResolveInfo> activities = pm.queryBroadcastReceivers(intent, 0);
221         for (ResolveInfo resolveInfo : activities) {
222             String packageName = resolveInfo.activityInfo.packageName;
223             if (PermissionHelper.hasPermission(context, packageName, MODIFY_ADSERVICES_STATE)
224                     || PermissionHelper.hasPermission(
225                             context, packageName, MODIFY_ADSERVICES_STATE_COMPAT)) {
226                 LogUtil.d(
227                         "Matching Broadcast receiver found for %s action in : %s",
228                         ACTION_ADSERVICES_NOTIFICATION_DISPLAYED, packageName);
229                 context.sendBroadcast(intent.setPackage(packageName));
230                 return;
231             }
232             LogUtil.w(
233                     "Matching Broadcast receiver found for %s action in : %s, but it has neither "
234                             + "%s nor %s permission",
235                     ACTION_ADSERVICES_NOTIFICATION_DISPLAYED,
236                     packageName,
237                     MODIFY_ADSERVICES_STATE,
238                     MODIFY_ADSERVICES_STATE_COMPAT);
239         }
240         LogUtil.w(
241                 "No explicit broadcast receiver available in system image to handle "
242                         + "%s action. Sending implicit broadcast instead.",
243                 ACTION_ADSERVICES_NOTIFICATION_DISPLAYED);
244         context.sendBroadcast(intent);
245     }
246 
247     /**
248      * Shows consent notification as the highest priority notification to the user.
249      *
250      * @param context Context which is used to display {@link NotificationCompat}
251      */
252     @SuppressWarnings("AvoidStaticContext") // UX class
showConsentNotification(@onNull Context context, boolean isEuDevice)253     public static void showConsentNotification(@NonNull Context context, boolean isEuDevice) {
254         LogUtil.d("Started requesting notification.");
255         UiStatsLogger.logRequestedNotification();
256 
257         boolean gaUxFeatureEnabled =
258                 UxStatesManager.getInstance().getFlag(KEY_GA_UX_FEATURE_ENABLED);
259 
260         NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
261         ConsentManager consentManager = ConsentManager.getInstance();
262         if (!notificationManager.areNotificationsEnabled()) {
263             LogUtil.d("Notification is disabled.");
264             recordNotificationDisplayed(context, gaUxFeatureEnabled, consentManager);
265             UiStatsLogger.logNotificationDisabled();
266             return;
267         }
268 
269         // Set OTA resources if it exists.
270         if (UxStatesManager.getInstance().getFlag(KEY_UI_OTA_STRINGS_FEATURE_ENABLED)
271                 || UxStatesManager.getInstance().getFlag(KEY_UI_OTA_RESOURCES_FEATURE_ENABLED)) {
272             OTAResourcesManager.applyOTAResources(context.getApplicationContext(), true);
273         }
274 
275         createNotificationChannel(context);
276         Notification notification =
277                 getNotification(context, isEuDevice, gaUxFeatureEnabled, consentManager);
278 
279         notificationManager.notify(NOTIFICATION_ID, notification);
280         recordNotificationDisplayed(context, gaUxFeatureEnabled, consentManager);
281 
282         // must setup consents after recording notification displayed data to ensure accurate UX in
283         // logs
284         setupConsents(context, isEuDevice, gaUxFeatureEnabled, consentManager);
285         UiStatsLogger.logNotificationDisplayed();
286         LogUtil.d("Notification was displayed.");
287     }
288 
289     @SuppressWarnings("AvoidStaticContext") // UX class
recordNotificationDisplayed( @onNull Context context, boolean gaUxFeatureEnabled, ConsentManager consentManager)290     private static void recordNotificationDisplayed(
291             @NonNull Context context, boolean gaUxFeatureEnabled, ConsentManager consentManager) {
292         if (UxStatesManager.getInstance().getFlag(KEY_RECORD_MANUAL_INTERACTION_ENABLED)
293                 && consentManager.getUserManualInteractionWithConsent()
294                         != MANUAL_INTERACTIONS_RECORDED) {
295             consentManager.recordUserManualInteractionWithConsent(NO_MANUAL_INTERACTIONS_RECORDED);
296         }
297 
298         if (isUxStatesReady()) {
299             switch (UxUtil.getUx()) {
300                 case GA_UX:
301                     if (UxStatesManager.getInstance().getFlag(KEY_PAS_UX_ENABLED)) {
302                         consentManager.recordPasNotificationDisplayed(true);
303                         break;
304                     }
305                     consentManager.recordGaUxNotificationDisplayed(true);
306                     break;
307                 case U18_UX:
308                     consentManager.setU18NotificationDisplayed(true);
309                     break;
310                 case BETA_UX:
311                     consentManager.recordNotificationDisplayed(true);
312                     break;
313                 default:
314                     break;
315             }
316         } else {
317             if (gaUxFeatureEnabled) {
318                 consentManager.recordGaUxNotificationDisplayed(true);
319             }
320             consentManager.recordNotificationDisplayed(true);
321         }
322     }
323 
324     @NonNull
325     @SuppressWarnings("AvoidStaticContext") // UX class
getNotification( @onNull Context context, boolean isEuDevice, boolean gaUxFeatureEnabled, ConsentManager consentManager)326     private static Notification getNotification(
327             @NonNull Context context,
328             boolean isEuDevice,
329             boolean gaUxFeatureEnabled,
330             ConsentManager consentManager) {
331         Notification notification;
332         if (isUxStatesReady()) {
333             switch (UxUtil.getUx()) {
334                 case GA_UX:
335                     if (UxStatesManager.getInstance().getFlag(KEY_PAS_UX_ENABLED)) {
336                         notification =
337                                 getPasConsentNotification(context, consentManager, isEuDevice);
338                         break;
339                     }
340                     notification = getGaV2ConsentNotification(context, isEuDevice);
341                     break;
342                 case U18_UX:
343                     notification = getU18ConsentNotification(context);
344                     break;
345                 case BETA_UX:
346                     notification = getConsentNotification(context, isEuDevice);
347                     break;
348                 default:
349                     notification = getGaV2ConsentNotification(context, isEuDevice);
350             }
351         } else {
352             if (gaUxFeatureEnabled) {
353                 notification = getGaV2ConsentNotification(context, isEuDevice);
354             } else {
355                 notification = getConsentNotification(context, isEuDevice);
356             }
357         }
358 
359         // make notification sticky (non-dismissible) for EuDevices when the GA UX feature is on
360         if (gaUxFeatureEnabled && isEuDevice) {
361             notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
362         }
363         return notification;
364     }
365 
366     // setup default consents based on information whether the device is EU or non-EU device and
367     // GA UX feature flag is enabled.
368     @SuppressWarnings("AvoidStaticContext") // UX class
setupConsents( @onNull Context context, boolean isEuDevice, boolean gaUxFeatureEnabled, ConsentManager consentManager)369     private static void setupConsents(
370             @NonNull Context context,
371             boolean isEuDevice,
372             boolean gaUxFeatureEnabled,
373             ConsentManager consentManager) {
374         if (isUxStatesReady()) {
375             switch (UxUtil.getUx()) {
376                 case GA_UX:
377                     if (isPasRenotifyUser(consentManager)) {
378                         // Is PAS renotify user, respect previous consents.
379                         break;
380                     }
381                     setUpGaConsent(context, isEuDevice, consentManager);
382                     break;
383                 case U18_UX:
384                     consentManager.recordMeasurementDefaultConsent(true);
385                     consentManager.enable(context, AdServicesApiType.MEASUREMENTS);
386                     break;
387                 case BETA_UX:
388                     if (!isEuDevice) {
389                         consentManager.enable(context);
390                     } else {
391                         consentManager.disable(context);
392                     }
393                     break;
394                 default:
395                     // Default behavior is GA UX.
396                     setUpGaConsent(context, isEuDevice, consentManager);
397                     break;
398             }
399         } else {
400             // Keep the feature flag at the upper level to make it easier to cleanup the code once
401             // the beta functionality is fully deprecated and abandoned.
402             if (gaUxFeatureEnabled) {
403                 setUpGaConsent(context, isEuDevice, consentManager);
404             } else {
405                 // For the ROW devices, set the consent to GIVEN (enabled).
406                 // For the EU devices, set the consent to REVOKED (disabled)
407                 if (!isEuDevice) {
408                     consentManager.enable(context);
409                 } else {
410                     consentManager.disable(context);
411                 }
412             }
413         }
414     }
415 
isPasRenotifyUser(ConsentManager consentManager)416     private static boolean isPasRenotifyUser(ConsentManager consentManager) {
417         return UxStatesManager.getInstance().getFlag(KEY_PAS_UX_ENABLED)
418                 && (isFledgeOrMsmtEnabled(consentManager)
419                         || consentManager.getUserManualInteractionWithConsent()
420                                 == MANUAL_INTERACTIONS_RECORDED);
421     }
422 
423     @SuppressWarnings("AvoidStaticContext") // UX class
getGaV2ConsentNotification( @onNull Context context, boolean isEuDevice)424     private static Notification getGaV2ConsentNotification(
425             @NonNull Context context, boolean isEuDevice) {
426         Intent intent = new Intent(context, ConsentNotificationActivity.class);
427 
428         PendingIntent pendingIntent =
429                 PendingIntent.getActivity(
430                         context, GA_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE);
431 
432         String bigText =
433                 isEuDevice
434                         ? context.getString(R.string.notificationUI_notification_ga_content_eu_v2)
435                         : context.getString(R.string.notificationUI_notification_ga_content_v2);
436 
437         NotificationCompat.BigTextStyle textStyle =
438                 new NotificationCompat.BigTextStyle().bigText(bigText);
439 
440         String contentTitle =
441                 context.getString(
442                         isEuDevice
443                                 ? R.string.notificationUI_notification_ga_title_eu_v2
444                                 : R.string.notificationUI_notification_ga_title_v2);
445         String contentText =
446                 context.getString(
447                         isEuDevice
448                                 ? R.string.notificationUI_notification_ga_content_eu_v2
449                                 : R.string.notificationUI_notification_ga_content_v2);
450 
451         NotificationCompat.Builder notification =
452                 new NotificationCompat.Builder(context, CHANNEL_ID)
453                         .setSmallIcon(R.drawable.ic_info_icon)
454                         .setContentTitle(contentTitle)
455                         .setContentText(contentText)
456                         .setStyle(textStyle)
457                         .setPriority(NOTIFICATION_PRIORITY)
458                         .setAutoCancel(true)
459                         .setContentIntent(pendingIntent);
460         return notification.build();
461     }
462 
463     /**
464      * Returns a {@link NotificationCompat.Builder} which can be used to display consent
465      * notification to the user.
466      *
467      * @param context {@link Context} which is used to prepare a {@link NotificationCompat}.
468      */
469     @SuppressWarnings("AvoidStaticContext") // UX class
getConsentNotification( @onNull Context context, boolean isEuDevice)470     private static Notification getConsentNotification(
471             @NonNull Context context, boolean isEuDevice) {
472         Intent intent = new Intent(context, ConsentNotificationActivity.class);
473 
474         PendingIntent pendingIntent =
475                 PendingIntent.getActivity(
476                         context, BETA_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE);
477         NotificationCompat.BigTextStyle textStyle =
478                 new NotificationCompat.BigTextStyle()
479                         .bigText(
480                                 isEuDevice
481                                         ? context.getString(
482                                                 R.string.notificationUI_notification_content_eu)
483                                         : context.getString(
484                                                 R.string.notificationUI_notification_content));
485         return new NotificationCompat.Builder(context, CHANNEL_ID)
486                 .setSmallIcon(R.drawable.ic_info_icon)
487                 .setContentTitle(
488                         context.getString(
489                                 isEuDevice
490                                         ? R.string.notificationUI_notification_title_eu
491                                         : R.string.notificationUI_notification_title))
492                 .setContentText(
493                         context.getString(
494                                 isEuDevice
495                                         ? R.string.notificationUI_notification_content_eu
496                                         : R.string.notificationUI_notification_content))
497                 .setStyle(textStyle)
498                 .setPriority(NOTIFICATION_PRIORITY)
499                 .setAutoCancel(true)
500                 .setContentIntent(pendingIntent)
501                 .build();
502     }
503 
504     /**
505      * Returns a {@link NotificationCompat.Builder} which can be used to display consent
506      * notification to U18 users.
507      *
508      * @param context {@link Context} which is used to prepare a {@link NotificationCompat}.
509      */
510     @SuppressWarnings("AvoidStaticContext") // UX class
getU18ConsentNotification(@onNull Context context)511     private static Notification getU18ConsentNotification(@NonNull Context context) {
512         Intent intent = new Intent(context, ConsentNotificationActivity.class);
513 
514         PendingIntent pendingIntent =
515                 PendingIntent.getActivity(
516                         context, U18_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE);
517         NotificationCompat.BigTextStyle textStyle =
518                 new NotificationCompat.BigTextStyle()
519                         .bigText(
520                                 context.getString(
521                                         R.string.notificationUI_u18_notification_content));
522         return new NotificationCompat.Builder(context, CHANNEL_ID)
523                 .setSmallIcon(R.drawable.ic_info_icon)
524                 .setContentTitle(context.getString(R.string.notificationUI_u18_notification_title))
525                 .setContentText(context.getString(R.string.notificationUI_u18_notification_content))
526                 .setStyle(textStyle)
527                 .setPriority(NOTIFICATION_PRIORITY)
528                 .setAutoCancel(true)
529                 .setContentIntent(pendingIntent)
530                 .build();
531     }
532 
533     @SuppressWarnings("AvoidStaticContext") // UX class
getPasConsentNotification( @onNull Context context, ConsentManager consentManager, boolean isEuDevice)534     private static Notification getPasConsentNotification(
535             @NonNull Context context, ConsentManager consentManager, boolean isEuDevice) {
536         boolean isRenotify = isFledgeOrMsmtEnabled(consentManager);
537         Intent intent = new Intent(context, ConsentNotificationActivity.class);
538         intent.putExtra(IS_RENOTIFY_KEY, isRenotify);
539         if (!FlagsFactory.getFlags().getAdServicesConsentBusinessLogicMigrationEnabled()) {
540             // isEuDevice argument here includes AdId disabled ROW users, which cannot be obtained
541             // within the notification activity from DeviceRegionProvider.
542             intent.putExtra(IS_STRICT_CONSENT_BEHAVIOR, isEuDevice);
543         }
544 
545         PendingIntent pendingIntent =
546                 PendingIntent.getActivity(
547                         context, GA_WITH_PAS_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE);
548 
549         String bigText =
550                 isRenotify
551                         ? context.getString(R.string.notificationUI_pas_re_notification_content)
552                         : context.getString(R.string.notificationUI_pas_notification_content);
553 
554         NotificationCompat.BigTextStyle textStyle =
555                 new NotificationCompat.BigTextStyle().bigText(bigText);
556 
557         String contentTitle =
558                 context.getString(
559                         isRenotify
560                                 ? R.string.notificationUI_pas_re_notification_title
561                                 : R.string.notificationUI_pas_notification_title);
562         String contentText =
563                 context.getString(
564                         isRenotify
565                                 ? R.string.notificationUI_pas_re_notification_content
566                                 : R.string.notificationUI_pas_notification_content);
567 
568         NotificationCompat.Builder notification =
569                 new NotificationCompat.Builder(context, CHANNEL_ID)
570                         .setSmallIcon(R.drawable.ic_info_icon)
571                         .setContentTitle(contentTitle)
572                         .setContentText(contentText)
573                         .setStyle(textStyle)
574                         .setPriority(NOTIFICATION_PRIORITY)
575                         .setAutoCancel(true)
576                         .setContentIntent(pendingIntent);
577         return notification.build();
578     }
579 
isFledgeOrMsmtEnabled(ConsentManager consentManager)580     private static boolean isFledgeOrMsmtEnabled(ConsentManager consentManager) {
581         return consentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()
582                 || consentManager.getConsent(AdServicesApiType.MEASUREMENTS).isGiven();
583     }
584 
585     @SuppressWarnings("AvoidStaticContext") // UX class
createNotificationChannel(@onNull Context context)586     private static void createNotificationChannel(@NonNull Context context) {
587         // TODO (b/230372892): styling -> adjust channels to use Android System labels.
588         int importance = NotificationManager.IMPORTANCE_HIGH;
589         NotificationChannel channel =
590                 new NotificationChannel(
591                         CHANNEL_ID,
592                         context.getString(R.string.settingsUI_main_view_title),
593                         importance);
594         channel.setDescription(context.getString(R.string.settingsUI_main_view_title));
595         NotificationManager notificationManager =
596                 context.getSystemService(NotificationManager.class);
597         notificationManager.createNotificationChannel(channel);
598     }
599 
600     @SuppressWarnings("AvoidStaticContext") // UX class
setUpGaConsent( @onNull Context context, boolean isEuDevice, ConsentManager consentManager)601     private static void setUpGaConsent(
602             @NonNull Context context, boolean isEuDevice, ConsentManager consentManager) {
603         if (isEuDevice) {
604             consentManager.recordTopicsDefaultConsent(false);
605             consentManager.recordFledgeDefaultConsent(false);
606             consentManager.recordMeasurementDefaultConsent(false);
607 
608             consentManager.disable(context, AdServicesApiType.TOPICS);
609             consentManager.disable(context, AdServicesApiType.FLEDGE);
610             consentManager.disable(context, AdServicesApiType.MEASUREMENTS);
611         } else {
612             consentManager.recordTopicsDefaultConsent(true);
613             consentManager.recordFledgeDefaultConsent(true);
614             consentManager.recordMeasurementDefaultConsent(true);
615 
616             consentManager.enable(context, AdServicesApiType.TOPICS);
617             consentManager.enable(context, AdServicesApiType.FLEDGE);
618             consentManager.enable(context, AdServicesApiType.MEASUREMENTS);
619         }
620     }
621 }
622