• 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.service.measurement.attribution;
18 
19 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_BACKGROUND_JOBS_EXECUTION_REPORTED__EXECUTION_RESULT_CODE__SKIP_FOR_EXTSERVICES_JOB_ON_TPLUS;
20 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_BACKGROUND_JOBS_EXECUTION_REPORTED__EXECUTION_RESULT_CODE__SKIP_FOR_KILL_SWITCH_ON;
21 import static com.android.adservices.spe.AdservicesJobInfo.MEASUREMENT_ATTRIBUTION_FALLBACK_JOB;
22 
23 import android.app.job.JobInfo;
24 import android.app.job.JobParameters;
25 import android.app.job.JobScheduler;
26 import android.app.job.JobService;
27 import android.content.ComponentName;
28 import android.content.Context;
29 
30 import com.android.adservices.LogUtil;
31 import com.android.adservices.concurrency.AdServicesExecutors;
32 import com.android.adservices.data.measurement.DatastoreManagerFactory;
33 import com.android.adservices.service.FlagsFactory;
34 import com.android.adservices.service.common.compat.ServiceCompatUtils;
35 import com.android.adservices.service.measurement.Trigger;
36 import com.android.adservices.service.measurement.reporting.DebugReportApi;
37 import com.android.adservices.service.measurement.reporting.DebugReportingJobService;
38 import com.android.adservices.spe.AdservicesJobServiceLogger;
39 import com.android.internal.annotations.VisibleForTesting;
40 
41 import java.util.concurrent.Executor;
42 
43 /**
44  * Fallback attribution job. The actual job execution logic is part of {@link
45  * AttributionJobHandler}.
46  */
47 public class AttributionFallbackJobService extends JobService {
48     private static final int MEASUREMENT_ATTRIBUTION_FALLBACK_JOB_ID =
49             MEASUREMENT_ATTRIBUTION_FALLBACK_JOB.getJobId();
50     private static final Executor sBackgroundExecutor = AdServicesExecutors.getBackgroundExecutor();
51 
52     @Override
onCreate()53     public void onCreate() {
54         LogUtil.d("AttributionFallbackJobService.onCreate");
55         super.onCreate();
56     }
57 
58     @Override
onStartJob(JobParameters params)59     public boolean onStartJob(JobParameters params) {
60         // Always ensure that the first thing this job does is check if it should be running, and
61         // cancel itself if it's not supposed to be.
62         if (ServiceCompatUtils.shouldDisableExtServicesJobOnTPlus(this)) {
63             LogUtil.d(
64                     "Disabling AttributionFallbackJobService job because it's running in"
65                             + " ExtServices on T+");
66             return skipAndCancelBackgroundJob(
67                     params,
68                     AD_SERVICES_BACKGROUND_JOBS_EXECUTION_REPORTED__EXECUTION_RESULT_CODE__SKIP_FOR_EXTSERVICES_JOB_ON_TPLUS);
69         }
70 
71         AdservicesJobServiceLogger.getInstance(this)
72                 .recordOnStartJob(MEASUREMENT_ATTRIBUTION_FALLBACK_JOB_ID);
73 
74         if (FlagsFactory.getFlags().getMeasurementAttributionFallbackJobKillSwitch()) {
75             LogUtil.e("AttributionFallbackJobService is disabled");
76             return skipAndCancelBackgroundJob(
77                     params,
78                     AD_SERVICES_BACKGROUND_JOBS_EXECUTION_REPORTED__EXECUTION_RESULT_CODE__SKIP_FOR_KILL_SWITCH_ON);
79         }
80 
81         LogUtil.d("AttributionFallbackJobService.onStartJob");
82         sBackgroundExecutor.execute(
83                 () -> {
84                     boolean success =
85                             new AttributionJobHandler(
86                                             DatastoreManagerFactory.getDatastoreManager(
87                                                     getApplicationContext()),
88                                             new DebugReportApi(
89                                                     getApplicationContext(),
90                                                     FlagsFactory.getFlags()))
91                                     .performPendingAttributions();
92                     DebugReportingJobService.scheduleIfNeeded(
93                             getApplicationContext(),
94                             /* forceSchedule */ true,
95                             /* isDebugReportApi */ false);
96 
97                     AdservicesJobServiceLogger.getInstance(AttributionFallbackJobService.this)
98                             .recordJobFinished(
99                                     MEASUREMENT_ATTRIBUTION_FALLBACK_JOB_ID, success, !success);
100 
101                     jobFinished(params, !success);
102                 });
103         return true;
104     }
105 
106     @Override
onStopJob(JobParameters params)107     public boolean onStopJob(JobParameters params) {
108         LogUtil.d("AttributionFallbackJobService.onStopJob");
109         boolean shouldRetry = true;
110 
111         AdservicesJobServiceLogger.getInstance(this)
112                 .recordOnStopJob(params, MEASUREMENT_ATTRIBUTION_FALLBACK_JOB_ID, shouldRetry);
113         return shouldRetry;
114     }
115 
116     /**
117      * Schedules {@link AttributionFallbackJobService} to observer {@link Trigger} content URI
118      * change.
119      */
120     @VisibleForTesting
schedule(Context context, JobScheduler jobScheduler)121     static void schedule(Context context, JobScheduler jobScheduler) {
122         final JobInfo job =
123                 new JobInfo.Builder(
124                                 MEASUREMENT_ATTRIBUTION_FALLBACK_JOB_ID,
125                                 new ComponentName(context, AttributionJobService.class))
126                         .setPeriodic(
127                                 FlagsFactory.getFlags()
128                                         .getMeasurementAttributionFallbackJobPeriodMs())
129                         .setPersisted(true)
130                         .build();
131         jobScheduler.schedule(job);
132     }
133 
134     /**
135      * Schedule Attribution Fallback Job if it is not already scheduled
136      *
137      * @param context the context
138      * @param forceSchedule flag to indicate whether to force rescheduling the job.
139      */
scheduleIfNeeded(Context context, boolean forceSchedule)140     public static void scheduleIfNeeded(Context context, boolean forceSchedule) {
141         if (FlagsFactory.getFlags().getMeasurementAttributionFallbackJobKillSwitch()) {
142             LogUtil.e("AttributionFallbackJobService is disabled, skip scheduling");
143             return;
144         }
145 
146         final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
147         if (jobScheduler == null) {
148             LogUtil.e("JobScheduler not found");
149             return;
150         }
151 
152         final JobInfo job = jobScheduler.getPendingJob(MEASUREMENT_ATTRIBUTION_FALLBACK_JOB_ID);
153         // Schedule if it hasn't been scheduled already or force rescheduling
154         if (job == null || forceSchedule) {
155             schedule(context, jobScheduler);
156             LogUtil.d("Scheduled AttributionFallbackJobService");
157         } else {
158             LogUtil.d("AttributionFallbackJobService already scheduled, skipping reschedule");
159         }
160     }
161 
skipAndCancelBackgroundJob(final JobParameters params, int skipReason)162     private boolean skipAndCancelBackgroundJob(final JobParameters params, int skipReason) {
163         final JobScheduler jobScheduler = this.getSystemService(JobScheduler.class);
164         if (jobScheduler != null) {
165             jobScheduler.cancel(MEASUREMENT_ATTRIBUTION_FALLBACK_JOB_ID);
166         }
167 
168         AdservicesJobServiceLogger.getInstance(this)
169                 .recordJobSkipped(MEASUREMENT_ATTRIBUTION_FALLBACK_JOB_ID, skipReason);
170 
171         // Tell the JobScheduler that the job has completed and does not need to be rescheduled.
172         jobFinished(params, false);
173 
174         // Returning false means that this job has completed its work.
175         return false;
176     }
177 }
178