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.spe;
18 
19 import static com.android.adservices.shared.spe.JobServiceConstants.JOB_ENABLED_STATUS_DISABLED_FOR_BACK_COMPAT_OTA;
20 import static com.android.adservices.spe.AdServicesJobInfo.FLEDGE_BACKGROUND_FETCH_JOB;
21 import static com.android.adservices.spe.AdServicesJobInfo.MDD_CELLULAR_CHARGING_PERIODIC_TASK_JOB;
22 import static com.android.adservices.spe.AdServicesJobInfo.MDD_CHARGING_PERIODIC_TASK_JOB;
23 import static com.android.adservices.spe.AdServicesJobInfo.MDD_MAINTENANCE_PERIODIC_TASK_JOB;
24 import static com.android.adservices.spe.AdServicesJobInfo.MDD_WIFI_CHARGING_PERIODIC_TASK_JOB;
25 import static com.android.adservices.spe.AdServicesJobInfo.MEASUREMENT_ASYNC_REGISTRATION_FALLBACK_JOB;
26 import static com.android.adservices.spe.AdServicesJobInfo.TOPICS_EPOCH_JOB;
27 
28 import android.app.job.JobParameters;
29 import android.os.Build;
30 
31 import androidx.annotation.RequiresApi;
32 
33 import com.android.adservices.LogUtil;
34 import com.android.adservices.service.Flags;
35 import com.android.adservices.service.FlagsFactory;
36 import com.android.adservices.service.common.compat.ServiceCompatUtils;
37 import com.android.adservices.shared.spe.framework.AbstractJobService;
38 import com.android.adservices.shared.spe.framework.JobServiceFactory;
39 import com.android.internal.annotations.VisibleForTesting;
40 
41 /** The Adservices' implementation of {@link AbstractJobService}. */
42 @RequiresApi(Build.VERSION_CODES.S)
43 public final class AdServicesJobService extends AbstractJobService {
44     @Override
getJobServiceFactory()45     protected JobServiceFactory getJobServiceFactory() {
46         return AdServicesJobServiceFactory.getInstance();
47     }
48 
49     /**
50      * Overrides {@link AbstractJobService#onStartJob(JobParameters)} to add the logic to cancel
51      * Android S- job in T+ build.
52      */
53     @Override
onStartJob(JobParameters params)54     public boolean onStartJob(JobParameters params) {
55         int jobId = params.getJobId();
56         if (ServiceCompatUtils.shouldDisableExtServicesJobOnTPlus(this)) {
57             getJobServiceFactory().getJobServiceLogger().recordOnStartJob(jobId);
58 
59             LogUtil.d("Disabling job %d because it's running in ExtServices on T+", jobId);
60             skipAndCancelBackgroundJob(params, JOB_ENABLED_STATUS_DISABLED_FOR_BACK_COMPAT_OTA);
61             return false;
62         }
63 
64         // Switch to the legacy job scheduling if SPE is disabled. Since job ID remains the same,
65         // the scheduled job will be cancelled and rescheduled with the legacy method.
66         //
67         // And after the job is rescheduled, it will execute once instantly so don't log execution
68         // stats here.
69         if (shouldRescheduleWithLegacyMethod(jobId)) {
70             LogUtil.d(
71                     "SPE is disabled. Reschedule SPE job instance of jobId=%d with its legacy"
72                             + " JobService scheduling method.",
73                     jobId);
74             AdServicesJobServiceFactory factory =
75                     (AdServicesJobServiceFactory) getJobServiceFactory();
76             factory.rescheduleJobWithLegacyMethod(jobId);
77             return false;
78         }
79 
80         return super.onStartJob(params);
81     }
82 
83     // Determine whether we should cancel and reschedule current job with the legacy JobService
84     // class. It could happen when SPE has a production issue.
85     //
86     // First batch pilot jobs: MddJobService, job ID = 11, 12, 13, 14.
87     // Second batch pilot jobs:
88     //     - EpochJobService, job ID = 2.
89     //     - BackgroundFetchJobService, job ID = 9.
90     //     - AsyncRegistrationFallbackJobService, job ID = 19.
91     @VisibleForTesting
shouldRescheduleWithLegacyMethod(int jobId)92     boolean shouldRescheduleWithLegacyMethod(int jobId) {
93         Flags flags = FlagsFactory.getFlags();
94 
95         if (isFirstBatchPilotJobDisabledForSpe(jobId, flags)) {
96             return true;
97         }
98 
99         if (isSecondBatchPilotJobDisabledForSpe(jobId, flags)) {
100             return true;
101         }
102 
103         return false;
104     }
105 
isFirstBatchPilotJobDisabledForSpe(int jobId, Flags flags)106     private boolean isFirstBatchPilotJobDisabledForSpe(int jobId, Flags flags) {
107         return (jobId == MDD_WIFI_CHARGING_PERIODIC_TASK_JOB.getJobId()
108                         || jobId == MDD_MAINTENANCE_PERIODIC_TASK_JOB.getJobId()
109                         || jobId == MDD_CHARGING_PERIODIC_TASK_JOB.getJobId()
110                         || jobId == MDD_CELLULAR_CHARGING_PERIODIC_TASK_JOB.getJobId())
111                 && !flags.getSpeOnPilotJobsEnabled();
112     }
113 
isSecondBatchPilotJobDisabledForSpe(int jobId, Flags flags)114     private boolean isSecondBatchPilotJobDisabledForSpe(int jobId, Flags flags) {
115         if ((jobId == TOPICS_EPOCH_JOB.getJobId() && !flags.getSpeOnEpochJobEnabled())
116                 || (jobId == FLEDGE_BACKGROUND_FETCH_JOB.getJobId()
117                         && !flags.getSpeOnBackgroundFetchJobEnabled())
118                 || (jobId == MEASUREMENT_ASYNC_REGISTRATION_FALLBACK_JOB.getJobId()
119                         && !flags.getSpeOnAsyncRegistrationFallbackJobEnabled())) {
120             return true;
121         }
122         return false;
123     }
124 }
125