1 /*
2  * Copyright (C) 2024 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.service.customaudience;
18 
19 import static com.android.adservices.service.Flags.FLEDGE_BACKGROUND_FETCH_JOB_FLEX_MS;
20 import static com.android.adservices.service.Flags.FLEDGE_BACKGROUND_FETCH_JOB_PERIOD_MS;
21 import static com.android.adservices.service.consent.AdServicesApiType.FLEDGE;
22 import static com.android.adservices.shared.proto.JobPolicy.BatteryType.BATTERY_TYPE_REQUIRE_NOT_LOW;
23 import static com.android.adservices.shared.spe.JobServiceConstants.JOB_ENABLED_STATUS_DISABLED_FOR_KILL_SWITCH_ON;
24 import static com.android.adservices.shared.spe.JobServiceConstants.JOB_ENABLED_STATUS_DISABLED_FOR_USER_CONSENT_REVOKED;
25 import static com.android.adservices.shared.spe.JobServiceConstants.JOB_ENABLED_STATUS_ENABLED;
26 import static com.android.adservices.shared.spe.framework.ExecutionResult.SUCCESS;
27 import static com.android.adservices.spe.AdServicesJobInfo.FLEDGE_BACKGROUND_FETCH_JOB;
28 
29 import android.annotation.RequiresApi;
30 import android.content.Context;
31 import android.os.Build;
32 
33 import com.android.adservices.LoggerFactory;
34 import com.android.adservices.concurrency.AdServicesExecutors;
35 import com.android.adservices.service.Flags;
36 import com.android.adservices.service.FlagsFactory;
37 import com.android.adservices.service.consent.ConsentManager;
38 import com.android.adservices.shared.proto.JobPolicy;
39 import com.android.adservices.shared.spe.framework.ExecutionResult;
40 import com.android.adservices.shared.spe.framework.ExecutionRuntimeParameters;
41 import com.android.adservices.shared.spe.framework.JobWorker;
42 import com.android.adservices.shared.spe.scheduling.BackoffPolicy;
43 import com.android.adservices.shared.spe.scheduling.JobSpec;
44 import com.android.adservices.spe.AdServicesJobScheduler;
45 import com.android.adservices.spe.AdServicesJobServiceFactory;
46 import com.android.internal.annotations.VisibleForTesting;
47 
48 import com.google.common.util.concurrent.ListenableFuture;
49 
50 /**
51  * Background fetch for FLEDGE Custom Audience API, executing periodic garbage collection and custom
52  * audience updates.
53  */
54 @RequiresApi(Build.VERSION_CODES.S)
55 public class BackgroundFetchJob implements JobWorker {
56     private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger();
57 
58     @Override
getExecutionFuture( Context context, ExecutionRuntimeParameters executionRuntimeParameters)59     public ListenableFuture<ExecutionResult> getExecutionFuture(
60             Context context, ExecutionRuntimeParameters executionRuntimeParameters) {
61         return BackgroundFetchWorker.getInstance(context)
62                 .runBackgroundFetch()
63                 .transform(voidResult -> SUCCESS, AdServicesExecutors.getBackgroundExecutor());
64     }
65 
66     @Override
getJobEnablementStatus()67     public int getJobEnablementStatus() {
68         Flags flags = FlagsFactory.getFlags();
69 
70         if (!flags.getFledgeBackgroundFetchEnabled()) {
71             sLogger.d("FLEDGE background fetch is disabled; skipping and cancelling job");
72             return JOB_ENABLED_STATUS_DISABLED_FOR_KILL_SWITCH_ON;
73         }
74 
75         if (flags.getFledgeCustomAudienceServiceKillSwitch()) {
76             sLogger.d("FLEDGE Custom Audience API is disabled ; skipping and cancelling job");
77             return JOB_ENABLED_STATUS_DISABLED_FOR_KILL_SWITCH_ON;
78         }
79 
80         // Skip the execution and cancel the job if user consent is revoked.
81         // Use the per-API consent with GA UX.
82         if (!ConsentManager.getInstance().getConsent(FLEDGE).isGiven()) {
83             sLogger.d("User Consent is revoked ; skipping and cancelling job");
84             return JOB_ENABLED_STATUS_DISABLED_FOR_USER_CONSENT_REVOKED;
85         }
86         return JOB_ENABLED_STATUS_ENABLED;
87     }
88 
89     /**
90      * Attempts to schedule the FLEDGE Background Fetch as a singleton periodic job if it is not
91      * already scheduled.
92      *
93      * <p>The background fetch primarily updates custom audiences' ads and bidding data. It also
94      * prunes the custom audience database of any expired data.
95      */
schedule(Flags flags)96     public static void schedule(Flags flags) {
97         // If SPE is not enabled, force to schedule the job with the old JobService.
98         if (!flags.getSpeOnBackgroundFetchJobEnabled()) {
99             sLogger.d("SPE is not enabled. Schedule the job with BackgroundFetchJobService.");
100             int resultCode =
101                     BackgroundFetchJobService.scheduleIfNeeded(flags, /* forceSchedule= */ false);
102 
103             AdServicesJobServiceFactory.getInstance()
104                     .getJobSchedulingLogger()
105                     .recordOnSchedulingLegacy(FLEDGE_BACKGROUND_FETCH_JOB.getJobId(), resultCode);
106             return;
107         }
108 
109         AdServicesJobScheduler.getInstance().schedule(createDefaultJobSpec());
110     }
111 
112     @VisibleForTesting
createDefaultJobSpec()113     static JobSpec createDefaultJobSpec() {
114         JobPolicy jobPolicy =
115                 JobPolicy.newBuilder()
116                         .setJobId(FLEDGE_BACKGROUND_FETCH_JOB.getJobId())
117                         .setBatteryType(BATTERY_TYPE_REQUIRE_NOT_LOW)
118                         .setRequireDeviceIdle(true)
119                         .setPeriodicJobParams(
120                                 JobPolicy.PeriodicJobParams.newBuilder()
121                                         .setPeriodicIntervalMs(
122                                                 FLEDGE_BACKGROUND_FETCH_JOB_PERIOD_MS)
123                                         .setFlexInternalMs(FLEDGE_BACKGROUND_FETCH_JOB_FLEX_MS)
124                                         .build())
125                         .setNetworkType(JobPolicy.NetworkType.NETWORK_TYPE_UNMETERED)
126                         .setIsPersisted(true)
127                         .build();
128 
129         BackoffPolicy backoffPolicy =
130                 new BackoffPolicy.Builder().setShouldRetryOnExecutionStop(true).build();
131 
132         return new JobSpec.Builder(jobPolicy).setBackoffPolicy(backoffPolicy).build();
133     }
134 }
135