1 /* 2 * Copyright (C) 2023 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.registration; 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_ASYNC_REGISTRATION_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.service.FlagsFactory; 33 import com.android.adservices.service.common.compat.ServiceCompatUtils; 34 import com.android.adservices.spe.AdservicesJobServiceLogger; 35 import com.android.internal.annotations.VisibleForTesting; 36 37 import java.time.Clock; 38 import java.time.Instant; 39 40 /** Fallback Job Service for servicing queued registration requests */ 41 public class AsyncRegistrationFallbackJobService extends JobService { 42 private static final int MEASUREMENT_ASYNC_REGISTRATION_FALLBACK_JOB_ID = 43 MEASUREMENT_ASYNC_REGISTRATION_FALLBACK_JOB.getJobId(); 44 45 @Override onStartJob(JobParameters params)46 public boolean onStartJob(JobParameters params) { 47 // Always ensure that the first thing this job does is check if it should be running, and 48 // cancel itself if it's not supposed to be. 49 if (ServiceCompatUtils.shouldDisableExtServicesJobOnTPlus(this)) { 50 LogUtil.d( 51 "Disabling AsyncRegistrationFallbackJobService job because it's running in" 52 + " ExtServices on T+"); 53 return skipAndCancelBackgroundJob( 54 params, 55 AD_SERVICES_BACKGROUND_JOBS_EXECUTION_REPORTED__EXECUTION_RESULT_CODE__SKIP_FOR_EXTSERVICES_JOB_ON_TPLUS); 56 } 57 58 AdservicesJobServiceLogger.getInstance(this) 59 .recordOnStartJob(MEASUREMENT_ASYNC_REGISTRATION_FALLBACK_JOB_ID); 60 61 if (FlagsFactory.getFlags().getAsyncRegistrationFallbackJobKillSwitch()) { 62 LogUtil.e("AsyncRegistrationFallbackJobService is disabled"); 63 return skipAndCancelBackgroundJob( 64 params, 65 AD_SERVICES_BACKGROUND_JOBS_EXECUTION_REPORTED__EXECUTION_RESULT_CODE__SKIP_FOR_KILL_SWITCH_ON); 66 } 67 68 Instant jobStartTime = Clock.systemUTC().instant(); 69 LogUtil.d( 70 "AsyncRegistrationFallbackJobService.onStartJob " + "at %s", 71 jobStartTime.toString()); 72 AsyncRegistrationQueueRunner asyncQueueRunner = 73 AsyncRegistrationQueueRunner.getInstance(getApplicationContext()); 74 75 AdServicesExecutors.getBackgroundExecutor() 76 .execute( 77 () -> { 78 asyncQueueRunner.runAsyncRegistrationQueueWorker(); 79 80 boolean shouldRetry = false; 81 AdservicesJobServiceLogger.getInstance( 82 AsyncRegistrationFallbackJobService.this) 83 .recordJobFinished( 84 MEASUREMENT_ASYNC_REGISTRATION_FALLBACK_JOB_ID, 85 /* isSuccessful */ true, 86 shouldRetry); 87 88 jobFinished(params, false); 89 }); 90 return true; 91 } 92 93 @Override onStopJob(JobParameters params)94 public boolean onStopJob(JobParameters params) { 95 LogUtil.d("AsyncRegistrationFallbackJobService.onStopJob"); 96 boolean shouldRetry = false; 97 98 AdservicesJobServiceLogger.getInstance(this) 99 .recordOnStopJob( 100 params, MEASUREMENT_ASYNC_REGISTRATION_FALLBACK_JOB_ID, shouldRetry); 101 return shouldRetry; 102 } 103 104 @VisibleForTesting schedule(Context context, JobScheduler jobScheduler)105 protected static void schedule(Context context, JobScheduler jobScheduler) { 106 final JobInfo job = 107 new JobInfo.Builder( 108 MEASUREMENT_ASYNC_REGISTRATION_FALLBACK_JOB_ID, 109 new ComponentName( 110 context, AsyncRegistrationFallbackJobService.class)) 111 .setRequiresBatteryNotLow(true) 112 .setPeriodic( 113 FlagsFactory.getFlags().getAsyncRegistrationJobQueueIntervalMs()) 114 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 115 .setPersisted(true) 116 .build(); 117 jobScheduler.schedule(job); 118 } 119 /** 120 * Schedule Fallback Async Registration Job Service if it is not already scheduled 121 * 122 * @param context the context 123 * @param forceSchedule flag to indicate whether to force rescheduling the job. 124 */ scheduleIfNeeded(Context context, boolean forceSchedule)125 public static void scheduleIfNeeded(Context context, boolean forceSchedule) { 126 if (FlagsFactory.getFlags().getAsyncRegistrationFallbackJobKillSwitch()) { 127 LogUtil.e("AsyncRegistrationFallbackJobService is disabled, skip scheduling"); 128 return; 129 } 130 131 final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); 132 if (jobScheduler == null) { 133 LogUtil.e("JobScheduler not found"); 134 return; 135 } 136 137 final JobInfo job = 138 jobScheduler.getPendingJob(MEASUREMENT_ASYNC_REGISTRATION_FALLBACK_JOB_ID); 139 // Schedule if it hasn't been scheduled already or force rescheduling 140 if (job == null || forceSchedule) { 141 schedule(context, jobScheduler); 142 LogUtil.d("Scheduled AsyncRegistrationFallbackJobService"); 143 } else { 144 LogUtil.d("AsyncRegistrationFallbackJobService already scheduled, skipping reschedule"); 145 } 146 } 147 skipAndCancelBackgroundJob(final JobParameters params, int skipReason)148 private boolean skipAndCancelBackgroundJob(final JobParameters params, int skipReason) { 149 final JobScheduler jobScheduler = this.getSystemService(JobScheduler.class); 150 if (jobScheduler != null) { 151 jobScheduler.cancel(MEASUREMENT_ASYNC_REGISTRATION_FALLBACK_JOB_ID); 152 } 153 154 AdservicesJobServiceLogger.getInstance(this) 155 .recordJobSkipped(MEASUREMENT_ASYNC_REGISTRATION_FALLBACK_JOB_ID, skipReason); 156 157 // Tell the JobScheduler that the job is done and does not need to be rescheduled 158 jobFinished(params, false); 159 160 // Returning false to reschedule this job. 161 return false; 162 } 163 } 164