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.measurement.registration;
18 
19 import static com.android.adservices.service.Flags.ASYNC_REGISTRATION_JOB_QUEUE_INTERVAL_MS;
20 import static com.android.adservices.service.measurement.util.JobLockHolder.Type.ASYNC_REGISTRATION_PROCESSING;
21 import static com.android.adservices.shared.proto.JobPolicy.BatteryType.BATTERY_TYPE_REQUIRE_NOT_LOW;
22 import static com.android.adservices.shared.proto.JobPolicy.NetworkType.NETWORK_TYPE_ANY;
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_ENABLED;
25 import static com.android.adservices.shared.spe.framework.ExecutionResult.SUCCESS;
26 import static com.android.adservices.spe.AdServicesJobInfo.MEASUREMENT_ASYNC_REGISTRATION_FALLBACK_JOB;
27 
28 import android.annotation.RequiresApi;
29 import android.content.Context;
30 import android.os.Build;
31 
32 import com.android.adservices.LoggerFactory;
33 import com.android.adservices.concurrency.AdServicesExecutors;
34 import com.android.adservices.service.FlagsFactory;
35 import com.android.adservices.service.measurement.util.JobLockHolder;
36 import com.android.adservices.shared.proto.JobPolicy;
37 import com.android.adservices.shared.spe.framework.ExecutionResult;
38 import com.android.adservices.shared.spe.framework.ExecutionRuntimeParameters;
39 import com.android.adservices.shared.spe.framework.JobWorker;
40 import com.android.adservices.shared.spe.scheduling.BackoffPolicy;
41 import com.android.adservices.shared.spe.scheduling.JobSpec;
42 import com.android.adservices.spe.AdServicesJobScheduler;
43 import com.android.adservices.spe.AdServicesJobServiceFactory;
44 import com.android.internal.annotations.VisibleForTesting;
45 
46 import com.google.common.util.concurrent.Futures;
47 import com.google.common.util.concurrent.ListenableFuture;
48 
49 /** Fallback Job Service for servicing queued registration requests. */
50 // TODO(b/328287543): Since Rb has released to R so functionally this class should support R. Due to
51 // Legacy issue, class such as BackgroundJobsManager and MddJobService which have to support R also
52 // have this annotation. It won't have production impact but is needed to bypass the build error.
53 @RequiresApi(Build.VERSION_CODES.S)
54 public final class AsyncRegistrationFallbackJob implements JobWorker {
55     private static final LoggerFactory.Logger sLogger = LoggerFactory.getMeasurementLogger();
56 
57     @Override
getExecutionFuture( Context context, ExecutionRuntimeParameters executionRuntimeParameters)58     public ListenableFuture<ExecutionResult> getExecutionFuture(
59             Context context, ExecutionRuntimeParameters executionRuntimeParameters) {
60         return Futures.submit(
61                 () -> {
62                     processAsyncRecords(context);
63                     return SUCCESS;
64                 },
65                 AdServicesExecutors.getBlockingExecutor());
66     }
67 
68     @Override
getJobEnablementStatus()69     public int getJobEnablementStatus() {
70         if (FlagsFactory.getFlags().getAsyncRegistrationFallbackJobKillSwitch()) {
71             return JOB_ENABLED_STATUS_DISABLED_FOR_KILL_SWITCH_ON;
72         }
73 
74         return JOB_ENABLED_STATUS_ENABLED;
75     }
76 
77     /** Schedules the {@link AsyncRegistrationFallbackJob}. */
schedule()78     public static void schedule() {
79         // If SPE is not enabled, force to schedule the job with the old JobService.
80         if (!FlagsFactory.getFlags().getSpeOnAsyncRegistrationFallbackJobEnabled()) {
81             sLogger.d(
82                     "SPE is not enabled. Schedule the job with"
83                             + " AsyncRegistrationFallbackJobService.");
84             int resultCode =
85                     AsyncRegistrationFallbackJobService.scheduleIfNeeded(
86                             /* forceSchedule= */ false);
87 
88             AdServicesJobServiceFactory.getInstance()
89                     .getJobSchedulingLogger()
90                     .recordOnSchedulingLegacy(
91                             MEASUREMENT_ASYNC_REGISTRATION_FALLBACK_JOB.getJobId(), resultCode);
92             return;
93         }
94 
95         AdServicesJobScheduler.getInstance().schedule(createDefaultJobSpec());
96     }
97 
98     @VisibleForTesting
createDefaultJobSpec()99     static JobSpec createDefaultJobSpec() {
100         JobPolicy jobPolicy =
101                 JobPolicy.newBuilder()
102                         .setJobId(MEASUREMENT_ASYNC_REGISTRATION_FALLBACK_JOB.getJobId())
103                         .setBatteryType(BATTERY_TYPE_REQUIRE_NOT_LOW)
104                         .setPeriodicJobParams(
105                                 JobPolicy.PeriodicJobParams.newBuilder()
106                                         .setPeriodicIntervalMs(
107                                                 ASYNC_REGISTRATION_JOB_QUEUE_INTERVAL_MS)
108                                         .build())
109                         .setNetworkType(NETWORK_TYPE_ANY)
110                         .setIsPersisted(true)
111                         .build();
112 
113         BackoffPolicy backoffPolicy =
114                 new BackoffPolicy.Builder().setShouldRetryOnExecutionStop(true).build();
115 
116         return new JobSpec.Builder(jobPolicy).setBackoffPolicy(backoffPolicy).build();
117     }
118 
119     @VisibleForTesting
processAsyncRecords(Context context)120     void processAsyncRecords(Context context) {
121         final JobLockHolder lock = JobLockHolder.getInstance(ASYNC_REGISTRATION_PROCESSING);
122         if (lock.tryLock()) {
123             try {
124                 AsyncRegistrationQueueRunner.getInstance(context).runAsyncRegistrationQueueWorker();
125                 return;
126             } finally {
127                 lock.unlock();
128             }
129         }
130 
131         sLogger.d("AsyncRegistrationFallbackJob did not acquire the lock");
132     }
133 }
134