• 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.reporting;
18 
19 import static com.android.adservices.spe.AdservicesJobInfo.MEASUREMENT_DEBUG_REPORT_API_JOB;
20 import static com.android.adservices.spe.AdservicesJobInfo.MEASUREMENT_DEBUG_REPORT_JOB;
21 
22 import android.app.job.JobInfo;
23 import android.app.job.JobParameters;
24 import android.app.job.JobScheduler;
25 import android.app.job.JobService;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.os.PersistableBundle;
29 
30 import com.android.adservices.LogUtil;
31 import com.android.adservices.concurrency.AdServicesExecutors;
32 import com.android.adservices.data.enrollment.EnrollmentDao;
33 import com.android.adservices.data.measurement.DatastoreManager;
34 import com.android.adservices.data.measurement.DatastoreManagerFactory;
35 import com.android.adservices.service.FlagsFactory;
36 import com.android.adservices.service.common.compat.ServiceCompatUtils;
37 import com.android.internal.annotations.VisibleForTesting;
38 
39 import java.util.concurrent.Executor;
40 
41 /**
42  * Main service for scheduling debug reporting jobs. The actual job execution logic is part of
43  * {@link DebugReportingJobHandler }, {@link EventReportingJobHandler } and {@link
44  * AggregateReportingJobHandler}
45  */
46 public final class DebugReportingJobService extends JobService {
47 
48     public static final String EXTRA_BUNDLE_IS_DEBUG_REPORT_API =
49             "EXTRA_BUNDLE_IS_DEBUG_REPORT_API";
50     private static final long DEBUG_REPORT_API_JOB_DELAY_MS = 3600 * 1000L;
51     private static final Executor sBlockingExecutor = AdServicesExecutors.getBlockingExecutor();
52 
53     @Override
onCreate()54     public void 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 DebugReportingJobService job because it's running in ExtServices on"
65                             + " T+");
66             return skipAndCancelBackgroundJob(params);
67         }
68 
69         if (FlagsFactory.getFlags().getMeasurementJobDebugReportingKillSwitch()) {
70             LogUtil.e("DebugReportingJobService is disabled");
71             return skipAndCancelBackgroundJob(params);
72         }
73         boolean isDebugReportApi = params.getExtras().getBoolean(EXTRA_BUNDLE_IS_DEBUG_REPORT_API);
74 
75         LogUtil.d("DebugReportingJobService.onStartJob: isDebugReportApi " + isDebugReportApi);
76         sBlockingExecutor.execute(
77                 () -> {
78                     sendReports(isDebugReportApi);
79                     jobFinished(params, false);
80                 });
81         return true;
82     }
83 
84     @Override
onStopJob(JobParameters params)85     public boolean onStopJob(JobParameters params) {
86         LogUtil.d("DebugReportingJobService.onStopJob");
87         return true;
88     }
89 
90     /** Schedules {@link DebugReportingJobService} */
91     @VisibleForTesting
schedule(Context context, JobScheduler jobScheduler, boolean isDebugReportApi)92     static void schedule(Context context, JobScheduler jobScheduler, boolean isDebugReportApi) {
93         final JobInfo job =
94                 new JobInfo.Builder(
95                                 getJobId(isDebugReportApi),
96                                 new ComponentName(context, DebugReportingJobService.class))
97                         .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
98                         .setOverrideDeadline(getJobDelay(isDebugReportApi))
99                         .setExtras(getBundle(isDebugReportApi))
100                         .build();
101         jobScheduler.schedule(job);
102     }
103 
104     /**
105      * Schedule Debug Reporting Job if it is not already scheduled
106      *
107      * @param context the context
108      * @param forceSchedule flag to indicate whether to force rescheduling the job.
109      * @param isDebugReportApi flag to indicate whether caller is DebugReportAPI.
110      */
scheduleIfNeeded( Context context, boolean forceSchedule, boolean isDebugReportApi)111     public static void scheduleIfNeeded(
112             Context context, boolean forceSchedule, boolean isDebugReportApi) {
113         if (FlagsFactory.getFlags().getMeasurementJobDebugReportingKillSwitch()) {
114             LogUtil.d("DebugReportingJobService is disabled, skip scheduling");
115             return;
116         }
117 
118         final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
119         if (jobScheduler == null) {
120             LogUtil.e("JobScheduler not found");
121             return;
122         }
123 
124         final JobInfo job = jobScheduler.getPendingJob(getJobId(isDebugReportApi));
125         // Schedule if it hasn't been scheduled already or force rescheduling
126         if (job == null || forceSchedule) {
127             schedule(context, jobScheduler, isDebugReportApi);
128             LogUtil.d("Scheduled DebugReportingJobService: isDebugReportApi " + isDebugReportApi);
129         } else {
130             LogUtil.d("DebugReportingJobService already scheduled, skipping reschedule");
131         }
132     }
133 
skipAndCancelBackgroundJob(final JobParameters params)134     private boolean skipAndCancelBackgroundJob(final JobParameters params) {
135         final JobScheduler jobScheduler = this.getSystemService(JobScheduler.class);
136         if (jobScheduler != null) {
137             jobScheduler.cancel(
138                     getJobId(params.getExtras().getBoolean(EXTRA_BUNDLE_IS_DEBUG_REPORT_API)));
139         }
140 
141         // Tell the JobScheduler that the job has completed and does not need to be rescheduled.
142         jobFinished(params, false);
143 
144         // Returning false means that this job has completed its work.
145         return false;
146     }
147 
sendReports(boolean isDebugReportApi)148     private void sendReports(boolean isDebugReportApi) {
149         EnrollmentDao enrollmentDao = EnrollmentDao.getInstance(getApplicationContext());
150         DatastoreManager datastoreManager =
151                 DatastoreManagerFactory.getDatastoreManager(getApplicationContext());
152         if (isDebugReportApi) {
153             new DebugReportingJobHandler(enrollmentDao, datastoreManager)
154                     .performScheduledPendingReports();
155         } else {
156             new EventReportingJobHandler(
157                             enrollmentDao, datastoreManager, ReportingStatus.UploadMethod.UNKNOWN)
158                     .setIsDebugInstance(true)
159                     .performScheduledPendingReportsInWindow(0, 0);
160             new AggregateReportingJobHandler(
161                             enrollmentDao, datastoreManager, ReportingStatus.UploadMethod.UNKNOWN)
162                     .setIsDebugInstance(true)
163                     .performScheduledPendingReportsInWindow(0, 0);
164         }
165     }
166 
getJobId(boolean isDebugReportApi)167     private static int getJobId(boolean isDebugReportApi) {
168         if (isDebugReportApi) {
169             return MEASUREMENT_DEBUG_REPORT_API_JOB.getJobId();
170         } else {
171             return MEASUREMENT_DEBUG_REPORT_JOB.getJobId();
172         }
173     }
174 
getJobDelay(boolean isDebugReportApi)175     private static long getJobDelay(boolean isDebugReportApi) {
176         if (isDebugReportApi) {
177             return DEBUG_REPORT_API_JOB_DELAY_MS;
178         } else {
179             return 1L;
180         }
181     }
182 
getBundle(boolean isDebugReportApi)183     private static PersistableBundle getBundle(boolean isDebugReportApi) {
184         PersistableBundle bundle = new PersistableBundle();
185         bundle.putBoolean(EXTRA_BUNDLE_IS_DEBUG_REPORT_API, isDebugReportApi);
186         return bundle;
187     }
188 }
189