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.topics; 18 19 import static com.android.adservices.service.Flags.TOPICS_EPOCH_JOB_FLEX_MS; 20 import static com.android.adservices.service.Flags.TOPICS_EPOCH_JOB_PERIOD_MS; 21 import static com.android.adservices.shared.proto.JobPolicy.BatteryType.BATTERY_TYPE_REQUIRE_CHARGING; 22 import static com.android.adservices.shared.spe.JobServiceConstants.JOB_ENABLED_STATUS_DISABLED_FOR_KILL_SWITCH_ON; 23 import static com.android.adservices.shared.spe.JobServiceConstants.JOB_ENABLED_STATUS_ENABLED; 24 import static com.android.adservices.shared.spe.framework.ExecutionResult.SUCCESS; 25 import static com.android.adservices.spe.AdServicesJobInfo.TOPICS_EPOCH_JOB; 26 27 import android.content.Context; 28 import android.os.Build; 29 30 import androidx.annotation.RequiresApi; 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.shared.proto.JobPolicy; 36 import com.android.adservices.shared.spe.framework.ExecutionResult; 37 import com.android.adservices.shared.spe.framework.ExecutionRuntimeParameters; 38 import com.android.adservices.shared.spe.framework.JobWorker; 39 import com.android.adservices.shared.spe.scheduling.JobSpec; 40 import com.android.adservices.spe.AdServicesJobScheduler; 41 import com.android.adservices.spe.AdServicesJobServiceFactory; 42 import com.android.internal.annotations.VisibleForTesting; 43 44 import com.google.common.util.concurrent.Futures; 45 import com.google.common.util.concurrent.ListenableFuture; 46 47 /** Epoch computation job. This will be run approximately once per epoch to compute Topics. */ 48 @RequiresApi(Build.VERSION_CODES.S) 49 public final class EpochJob implements JobWorker { 50 @Override getExecutionFuture( Context context, ExecutionRuntimeParameters executionRuntimeParameters)51 public ListenableFuture<ExecutionResult> getExecutionFuture( 52 Context context, ExecutionRuntimeParameters executionRuntimeParameters) { 53 return Futures.submit( 54 () -> { 55 TopicsWorker.getInstance().computeEpoch(); 56 return SUCCESS; 57 }, 58 AdServicesExecutors.getBackgroundExecutor()); 59 } 60 61 @Override getJobEnablementStatus()62 public int getJobEnablementStatus() { 63 if (FlagsFactory.getFlags().getTopicsKillSwitch()) { 64 LoggerFactory.getTopicsLogger() 65 .e("Topics API is disabled, skipping and cancelling EpochJobService"); 66 return JOB_ENABLED_STATUS_DISABLED_FOR_KILL_SWITCH_ON; 67 } 68 69 return JOB_ENABLED_STATUS_ENABLED; 70 } 71 72 /** Schedules the {@link EpochJob}. */ schedule()73 public static void schedule() { 74 // If SPE is not enabled, force to schedule the job with the old JobService. 75 if (!FlagsFactory.getFlags().getSpeOnEpochJobEnabled()) { 76 LoggerFactory.getTopicsLogger() 77 .d("SPE is not enabled. Schedule the job with EpochJobService."); 78 int resultCode = EpochJobService.scheduleIfNeeded(/* forceSchedule= */ false); 79 80 AdServicesJobServiceFactory.getInstance() 81 .getJobSchedulingLogger() 82 .recordOnSchedulingLegacy(TOPICS_EPOCH_JOB.getJobId(), resultCode); 83 return; 84 } 85 86 AdServicesJobScheduler.getInstance().schedule(createDefaultJobSpec()); 87 } 88 89 @VisibleForTesting createDefaultJobSpec()90 static JobSpec createDefaultJobSpec() { 91 JobPolicy jobPolicy = 92 JobPolicy.newBuilder() 93 .setJobId(TOPICS_EPOCH_JOB.getJobId()) 94 .setBatteryType(BATTERY_TYPE_REQUIRE_CHARGING) 95 .setIsPersisted(true) 96 .setPeriodicJobParams( 97 JobPolicy.PeriodicJobParams.newBuilder() 98 .setPeriodicIntervalMs(TOPICS_EPOCH_JOB_PERIOD_MS) 99 .setFlexInternalMs(TOPICS_EPOCH_JOB_FLEX_MS) 100 .build()) 101 .build(); 102 103 return new JobSpec.Builder(jobPolicy).build(); 104 } 105 } 106