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