• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.data.user;
18 
19 import static android.app.job.JobScheduler.RESULT_FAILURE;
20 
21 import android.app.job.JobInfo;
22 import android.app.job.JobParameters;
23 import android.app.job.JobScheduler;
24 import android.app.job.JobService;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.util.Log;
28 
29 import com.android.ondevicepersonalization.services.OnDevicePersonalizationConfig;
30 import com.android.ondevicepersonalization.services.OnDevicePersonalizationExecutors;
31 
32 import com.google.common.util.concurrent.FutureCallback;
33 import com.google.common.util.concurrent.Futures;
34 import com.google.common.util.concurrent.ListenableFuture;
35 
36 /**
37  * JobService to collect user data in the background thread.
38  */
39 public class UserDataCollectionJobService extends JobService {
40     public static final String TAG = "UserDataCollectionJobService";
41     // 4-hour interval.
42     private static final long PERIOD_SECONDS = 14400;
43     private ListenableFuture<Void> mFuture;
44     private UserDataCollector mUserDataCollector;
45     private RawUserData mUserData;
46 
47     /**
48      * Schedules a unique instance of UserDataCollectionJobService to be run.
49      */
schedule(Context context)50     public static int schedule(Context context) {
51         JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
52         if (jobScheduler.getPendingJob(
53                 OnDevicePersonalizationConfig.USER_DATA_COLLECTION_ID) != null) {
54             Log.d(TAG, "Job is already scheduled. Doing nothing,");
55             return RESULT_FAILURE;
56         }
57         ComponentName serviceComponent = new ComponentName(context,
58                 UserDataCollectionJobService.class);
59         JobInfo.Builder builder = new JobInfo.Builder(
60                 OnDevicePersonalizationConfig.USER_DATA_COLLECTION_ID, serviceComponent);
61 
62         // Constraints
63         builder.setRequiresDeviceIdle(true);
64         builder.setRequiresBatteryNotLow(true);
65         builder.setRequiresStorageNotLow(true);
66         builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE);
67         builder.setPeriodic(1000 * PERIOD_SECONDS); // JobScheduler uses Milliseconds.
68         // persist this job across boots
69         builder.setPersisted(true);
70 
71         return jobScheduler.schedule(builder.build());
72     }
73 
74     @Override
onStartJob(JobParameters params)75     public boolean onStartJob(JobParameters params) {
76         // TODO(b/265856477): return false to disable data collection if kid status is enabled.
77         Log.d(TAG, "onStartJob()");
78         mUserDataCollector = UserDataCollector.getInstance(this);
79         mUserData = RawUserData.getInstance();
80         mFuture = Futures.submit(new Runnable() {
81             @Override
82             public void run() {
83                 Log.d(TAG, "Running user data collection job");
84                 try {
85                     // TODO(b/262749958): add multi-threading support if necessary.
86                     mUserDataCollector.updateUserData(mUserData);
87                 } catch (Exception e) {
88                     Log.e(TAG, "Failed to collect user data", e);
89                 }
90             }
91         }, OnDevicePersonalizationExecutors.getBackgroundExecutor());
92 
93         Futures.addCallback(
94                 mFuture,
95                 new FutureCallback<Void>() {
96                     @Override
97                     public void onSuccess(Void result) {
98                         Log.d(TAG, "User data collection job completed.");
99                         jobFinished(params, /* wantsReschedule = */ false);
100                     }
101 
102                     @Override
103                     public void onFailure(Throwable t) {
104                         Log.e(TAG, "Failed to handle JobService: " + params.getJobId(), t);
105                         //  When failure, also tell the JobScheduler that the job has completed and
106                         // does not need to be rescheduled.
107                         jobFinished(params, /* wantsReschedule = */ false);
108                     }
109                 },
110                 OnDevicePersonalizationExecutors.getBackgroundExecutor());
111 
112         return true;
113     }
114 
115     @Override
onStopJob(JobParameters params)116     public boolean onStopJob(JobParameters params) {
117         if (mFuture != null) {
118             mFuture.cancel(true);
119         }
120         // Reschedule the job since it ended before finishing
121         return true;
122     }
123 }
124