1 /* 2 * Copyright (C) 2025 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.ondevicepersonalization.services.download.mdd; 18 19 20 import static com.android.adservices.shared.spe.JobServiceConstants.JOB_ENABLED_STATUS_DISABLED_FOR_KILL_SWITCH_ON; 21 import static com.android.adservices.shared.spe.JobServiceConstants.JOB_ENABLED_STATUS_DISABLED_FOR_USER_CONSENT_REVOKED; 22 import static com.android.adservices.shared.spe.JobServiceConstants.JOB_ENABLED_STATUS_ENABLED; 23 24 import static com.google.android.libraries.mobiledatadownload.TaskScheduler.WIFI_CHARGING_PERIODIC_TASK; 25 26 import android.content.Context; 27 28 import com.android.adservices.shared.spe.framework.ExecutionResult; 29 import com.android.adservices.shared.spe.framework.ExecutionRuntimeParameters; 30 import com.android.adservices.shared.spe.framework.JobWorker; 31 import com.android.adservices.shared.spe.scheduling.BackoffPolicy; 32 import com.android.internal.annotations.VisibleForTesting; 33 import com.android.ondevicepersonalization.internal.util.LoggerFactory; 34 import com.android.ondevicepersonalization.services.Flags; 35 import com.android.ondevicepersonalization.services.FlagsFactory; 36 import com.android.ondevicepersonalization.services.OnDevicePersonalizationExecutors; 37 import com.android.ondevicepersonalization.services.data.user.UserPrivacyStatus; 38 import com.android.ondevicepersonalization.services.download.OnDevicePersonalizationDownloadProcessingJob; 39 40 import com.google.android.libraries.mobiledatadownload.tracing.PropagatedFutures; 41 import com.google.common.util.concurrent.ListenableFuture; 42 import com.google.common.util.concurrent.ListeningExecutorService; 43 44 /** 45 * MDD JobService. This will download MDD files in background tasks. 46 */ 47 public final class MddJob implements JobWorker { 48 private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger(); 49 private static final String TAG = "MddJob"; 50 51 private final String mMddTaskTag; 52 private final Injector mInjector; 53 MddJob(String mddTaskTag)54 public MddJob(String mddTaskTag) { 55 mInjector = new Injector(); 56 mMddTaskTag = mddTaskTag; 57 } 58 59 @VisibleForTesting MddJob(String mddTaskTag, Injector injector)60 public MddJob(String mddTaskTag, Injector injector) { 61 mInjector = injector; 62 mMddTaskTag = mddTaskTag; 63 } 64 65 static class Injector { getBackgroundExecutor()66 ListeningExecutorService getBackgroundExecutor() { 67 return OnDevicePersonalizationExecutors.getBackgroundExecutor(); 68 } 69 getFlags()70 Flags getFlags() { 71 return FlagsFactory.getFlags(); 72 } 73 } 74 75 @Override getExecutionFuture( Context context, ExecutionRuntimeParameters executionRuntimeParameters)76 public ListenableFuture<ExecutionResult> getExecutionFuture( 77 Context context, ExecutionRuntimeParameters executionRuntimeParameters) { 78 ListenableFuture<Void> handleTaskFuture = 79 PropagatedFutures.submitAsync( 80 () -> MobileDataDownloadFactory.getMdd(context).handleTask(mMddTaskTag), 81 mInjector.getBackgroundExecutor()); 82 83 return PropagatedFutures.transform(handleTaskFuture, unused -> { 84 if (WIFI_CHARGING_PERIODIC_TASK.equals(mMddTaskTag)) { 85 OnDevicePersonalizationDownloadProcessingJob.schedule(context); 86 } 87 return ExecutionResult.SUCCESS; 88 }, mInjector.getBackgroundExecutor()); 89 } 90 91 @Override 92 public ListenableFuture<Void> getExecutionStopFuture( 93 Context context, ExecutionRuntimeParameters executionRuntimeParameters) { 94 // Attempt to process any data downloaded before the worker was stopped. 95 return PropagatedFutures.submit(() -> { 96 if (WIFI_CHARGING_PERIODIC_TASK.equals(mMddTaskTag)) { 97 OnDevicePersonalizationDownloadProcessingJob.schedule(context); 98 } 99 }, 100 mInjector.getBackgroundExecutor()); 101 } 102 103 @Override 104 public int getJobEnablementStatus() { 105 if (mInjector.getFlags().getGlobalKillSwitch()) { 106 sLogger.d(TAG + ": GlobalKillSwitch enabled, skip execution of MddJob."); 107 return JOB_ENABLED_STATUS_DISABLED_FOR_KILL_SWITCH_ON; 108 } 109 if (!mInjector.getFlags().getSpeOnMddJobEnabled()) { 110 sLogger.d(TAG + ": mdd is disabled; skipping and cancelling job"); 111 return JOB_ENABLED_STATUS_DISABLED_FOR_KILL_SWITCH_ON; 112 } 113 if (UserPrivacyStatus.getInstance().isProtectedAudienceAndMeasurementBothDisabled()) { 114 sLogger.d(TAG + ": consent revoked; skipping, cancelling job"); 115 return JOB_ENABLED_STATUS_DISABLED_FOR_USER_CONSENT_REVOKED; 116 } 117 return JOB_ENABLED_STATUS_ENABLED; 118 } 119 120 @Override 121 public BackoffPolicy getBackoffPolicy() { 122 return new BackoffPolicy.Builder().setShouldRetryOnExecutionStop(true).build(); 123 } 124 } 125