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.ondevicepersonalization.services.maintenance; 18 19 import static com.android.adservices.shared.proto.JobPolicy.BatteryType.BATTERY_TYPE_REQUIRE_NOT_LOW; 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_ENABLED; 22 import static com.android.ondevicepersonalization.services.OnDevicePersonalizationConfig.MAINTENANCE_TASK_JOB_ID; 23 24 import android.content.ComponentName; 25 import android.content.Context; 26 27 import com.android.adservices.shared.proto.JobPolicy; 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.adservices.shared.spe.scheduling.JobSpec; 33 import com.android.adservices.shared.util.Clock; 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.ondevicepersonalization.internal.util.LoggerFactory; 36 import com.android.ondevicepersonalization.services.FlagsFactory; 37 import com.android.ondevicepersonalization.services.OnDevicePersonalizationExecutors; 38 import com.android.ondevicepersonalization.services.data.errors.AggregatedErrorCodesLogger; 39 import com.android.ondevicepersonalization.services.data.events.EventsDao; 40 import com.android.ondevicepersonalization.services.data.vendor.OnDevicePersonalizationVendorDataDao; 41 import com.android.ondevicepersonalization.services.manifest.AppManifestConfigHelper; 42 import com.android.ondevicepersonalization.services.sharedlibrary.spe.OdpJobScheduler; 43 import com.android.ondevicepersonalization.services.sharedlibrary.spe.OdpJobServiceFactory; 44 45 import com.google.common.util.concurrent.Futures; 46 import com.google.common.util.concurrent.ListenableFuture; 47 48 import java.util.List; 49 50 /** The background job to handle the OnDevicePersonalization maintenance. */ 51 public final class OnDevicePersonalizationMaintenanceJob implements JobWorker { 52 @VisibleForTesting static final long PERIOD_MILLIS = 86400 * 1_000; // 24 hours 53 54 private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger(); 55 private static final String TAG = OnDevicePersonalizationMaintenanceJob.class.getSimpleName(); 56 57 // The maximum deletion timeframe is 63 days. 58 // Set parameter to 60 days to account for job scheduler delays. 59 private static final long MAXIMUM_DELETION_TIMEFRAME_MILLIS = 5_184_000_000L; 60 61 @Override getExecutionFuture( Context context, ExecutionRuntimeParameters executionRuntimeParameters)62 public ListenableFuture<ExecutionResult> getExecutionFuture( 63 Context context, ExecutionRuntimeParameters executionRuntimeParameters) { 64 return Futures.submit( 65 () -> { 66 cleanupVendorData(context); 67 68 return ExecutionResult.SUCCESS; 69 }, 70 OnDevicePersonalizationExecutors.getBackgroundExecutor()); 71 } 72 73 @Override getJobEnablementStatus()74 public int getJobEnablementStatus() { 75 if (FlagsFactory.getFlags().getGlobalKillSwitch()) { 76 sLogger.d( 77 TAG 78 + ": GlobalKillSwitch enabled, skip execution of" 79 + " OnDevicePersonalizationMaintenanceJob."); 80 return JOB_ENABLED_STATUS_DISABLED_FOR_KILL_SWITCH_ON; 81 } 82 83 return JOB_ENABLED_STATUS_ENABLED; 84 } 85 86 @Override getBackoffPolicy()87 public BackoffPolicy getBackoffPolicy() { 88 return new BackoffPolicy.Builder().setShouldRetryOnExecutionStop(true).build(); 89 } 90 91 /** Schedules a unique instance of {@link OnDevicePersonalizationMaintenanceJob}. */ schedule(Context context)92 public static void schedule(Context context) { 93 // If SPE is not enabled, force to schedule the job with the old JobService. 94 if (!FlagsFactory.getFlags().getSpePilotJobEnabled()) { 95 sLogger.d( 96 "SPE is not enabled. Schedule the job with" 97 + " OnDevicePersonalizationMaintenanceJobService."); 98 99 int resultCode = 100 OnDevicePersonalizationMaintenanceJobService.schedule( 101 context, /* forceSchedule= */ false); 102 OdpJobServiceFactory.getInstance(context) 103 .getJobSchedulingLogger() 104 .recordOnSchedulingLegacy(MAINTENANCE_TASK_JOB_ID, resultCode); 105 106 return; 107 } 108 109 OdpJobScheduler.getInstance(context).schedule(context, createDefaultJobSpec()); 110 } 111 112 @VisibleForTesting createDefaultJobSpec()113 static JobSpec createDefaultJobSpec() { 114 JobPolicy jobPolicy = 115 JobPolicy.newBuilder() 116 .setJobId(MAINTENANCE_TASK_JOB_ID) 117 .setBatteryType(BATTERY_TYPE_REQUIRE_NOT_LOW) 118 .setPeriodicJobParams( 119 JobPolicy.PeriodicJobParams.newBuilder() 120 .setPeriodicIntervalMs(PERIOD_MILLIS) 121 .build()) 122 .setIsPersisted(true) 123 .build(); 124 125 return new JobSpec.Builder(jobPolicy).build(); 126 } 127 128 @VisibleForTesting deleteEventsAndQueries(Context context)129 void deleteEventsAndQueries(Context context) { 130 EventsDao eventsDao = EventsDao.getInstance(context); 131 // Cleanup event and queries table. 132 eventsDao.deleteEventsAndQueries( 133 Clock.getInstance().currentTimeMillis() - MAXIMUM_DELETION_TIMEFRAME_MILLIS); 134 } 135 136 @VisibleForTesting cleanupVendorData(Context context)137 void cleanupVendorData(Context context) throws Exception { 138 List<ComponentName> services = 139 AppManifestConfigHelper.getOdpServices(context, /* enrolledOnly= */ true); 140 141 OnDevicePersonalizationVendorDataDao.deleteVendorTables(context, services); 142 deleteEventsAndQueries(context); 143 AggregatedErrorCodesLogger.cleanupErrorData(context); 144 } 145 } 146