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