• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.reset;
18 
19 import static android.app.job.JobScheduler.RESULT_SUCCESS;
20 
21 import static com.android.adservices.shared.spe.JobServiceConstants.SCHEDULING_RESULT_CODE_FAILED;
22 import static com.android.adservices.shared.spe.JobServiceConstants.SCHEDULING_RESULT_CODE_SKIPPED;
23 import static com.android.adservices.shared.spe.JobServiceConstants.SCHEDULING_RESULT_CODE_SUCCESSFUL;
24 import static com.android.ondevicepersonalization.services.OnDevicePersonalizationConfig.RESET_DATA_JOB_ID;
25 
26 import android.app.job.JobInfo;
27 import android.app.job.JobParameters;
28 import android.app.job.JobScheduler;
29 import android.app.job.JobService;
30 import android.content.ComponentName;
31 import android.content.Context;
32 
33 import com.android.adservices.shared.spe.JobServiceConstants;
34 import com.android.ondevicepersonalization.internal.util.LoggerFactory;
35 import com.android.ondevicepersonalization.services.Flags;
36 import com.android.ondevicepersonalization.services.FlagsFactory;
37 import com.android.ondevicepersonalization.services.OnDevicePersonalizationApplication;
38 import com.android.ondevicepersonalization.services.OnDevicePersonalizationExecutors;
39 import com.android.ondevicepersonalization.services.statsd.joblogging.OdpJobServiceLogger;
40 
41 import com.google.common.util.concurrent.FutureCallback;
42 import com.google.common.util.concurrent.Futures;
43 import com.google.common.util.concurrent.ListenableFuture;
44 
45 /**
46  * JobService to handle the OnDevicePersonalization maintenance
47  */
48 public class ResetDataJobService extends JobService {
49     private static final String TAG = ResetDataJobService.class.getSimpleName();
50     private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
51     private static final long MILLIS = 1000;
52     private ListenableFuture<Void> mFuture;
53 
54     /** Schedule the Reset job. */
55     @JobServiceConstants.JobSchedulingResultCode
schedule(boolean forceSchedule)56     public static int schedule(boolean forceSchedule) {
57         Flags flags = FlagsFactory.getFlags();
58         Context context = OnDevicePersonalizationApplication.getAppContext();
59         JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
60         if (jobScheduler == null) {
61             sLogger.e(TAG, "Failed to get job scheduler from system service.");
62             return SCHEDULING_RESULT_CODE_FAILED;
63         }
64 
65         if (!forceSchedule && jobScheduler.getPendingJob(RESET_DATA_JOB_ID) != null) {
66             sLogger.d(TAG + ": Job is already scheduled. Doing nothing,");
67             return SCHEDULING_RESULT_CODE_SKIPPED;
68         }
69 
70         ComponentName service = new ComponentName(context, ResetDataJobService.class);
71         JobInfo jobInfo = new JobInfo.Builder(RESET_DATA_JOB_ID, service)
72                 .setMinimumLatency(flags.getResetDataDelaySeconds() * MILLIS)
73                 .setOverrideDeadline(flags.getResetDataDeadlineSeconds() * MILLIS)
74                 .setRequiresBatteryNotLow(true)
75                 .setPersisted(true)
76                 .build();
77 
78         int schedulingResult = jobScheduler.schedule(jobInfo);
79         return RESULT_SUCCESS == schedulingResult ? SCHEDULING_RESULT_CODE_SUCCESSFUL
80                 : SCHEDULING_RESULT_CODE_FAILED;
81     }
82 
83     @Override
onStartJob(JobParameters params)84     public boolean onStartJob(JobParameters params) {
85         sLogger.d(TAG + ": onStartJob()");
86         OdpJobServiceLogger.getInstance(this).recordOnStartJob(
87                 RESET_DATA_JOB_ID);
88 
89         // Reschedule jobs with SPE if it's enabled. Note scheduled jobs by this
90         // ResetDataJobService will be cancelled for the same job ID.
91         if (FlagsFactory.getFlags().getSpeOnResetDataJobEnabled()) {
92             sLogger.d(
93                     "SPE is enabled. Reschedule ResetDataJobService with ResetDataJob.");
94             ResetDataJob.schedule(/* context */ this);
95             return false;
96         }
97 
98         mFuture = Futures.submit(new Runnable() {
99             @Override
100             public void run() {
101                 sLogger.d(TAG + ": Running reset job");
102                 try {
103                     ResetDataTask.deleteMeasurementData();
104                 } catch (Exception e) {
105                     sLogger.e(TAG + ": Failed to delete data", e);
106                 }
107             }
108         }, OnDevicePersonalizationExecutors.getBackgroundExecutor());
109 
110         Futures.addCallback(
111                 mFuture,
112                 new FutureCallback<Void>() {
113                     @Override
114                     public void onSuccess(Void result) {
115                         sLogger.d(TAG + ": Reset job completed.");
116                         boolean wantsReschedule = false;
117                         OdpJobServiceLogger.getInstance(
118                                 ResetDataJobService.this)
119                                 .recordJobFinished(
120                                         RESET_DATA_JOB_ID,
121                                         /* isSuccessful= */ true,
122                                         wantsReschedule);
123                         // Tell the JobScheduler that the job has completed and does not needs to be
124                         // rescheduled.
125                         jobFinished(params, wantsReschedule);
126                     }
127 
128                     @Override
129                     public void onFailure(Throwable t) {
130                         sLogger.e(TAG + ": Failed to handle JobService: " + params.getJobId(), t);
131                         boolean wantsReschedule = false;
132                         OdpJobServiceLogger.getInstance(
133                                 ResetDataJobService.this)
134                                 .recordJobFinished(
135                                         RESET_DATA_JOB_ID,
136                                         /* isSuccessful= */ false,
137                                         wantsReschedule);
138                         //  When failure, also tell the JobScheduler that the job has completed and
139                         // does not need to be rescheduled.
140                         jobFinished(params, wantsReschedule);
141                     }
142                 },
143                 OnDevicePersonalizationExecutors.getBackgroundExecutor());
144 
145         return true;
146     }
147 
148     @Override
onStopJob(JobParameters params)149     public boolean onStopJob(JobParameters params) {
150         if (mFuture != null) {
151             mFuture.cancel(true);
152         }
153         // Reschedule the job since it ended before finishing
154         boolean wantsReschedule = true;
155         OdpJobServiceLogger.getInstance(this)
156                 .recordOnStopJob(
157                         params,
158                         RESET_DATA_JOB_ID,
159                         wantsReschedule);
160         return wantsReschedule;
161     }
162 }
163