• 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.download.mdd;
18 
19 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_BACKGROUND_JOBS_EXECUTION_REPORTED__EXECUTION_RESULT_CODE__SKIP_FOR_KILL_SWITCH_ON;
20 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_BACKGROUND_JOBS_EXECUTION_REPORTED__EXECUTION_RESULT_CODE__SKIP_FOR_PERSONALIZATION_NOT_ENABLED;
21 import static com.android.ondevicepersonalization.services.download.mdd.MddTaskScheduler.getMddTaskTag;
22 
23 import static com.google.android.libraries.mobiledatadownload.TaskScheduler.WIFI_CHARGING_PERIODIC_TASK;
24 
25 import android.app.job.JobParameters;
26 import android.app.job.JobScheduler;
27 import android.app.job.JobService;
28 
29 import com.android.internal.annotations.VisibleForTesting;
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.OnDevicePersonalizationExecutors;
34 import com.android.ondevicepersonalization.services.data.user.UserPrivacyStatus;
35 import com.android.ondevicepersonalization.services.download.OnDevicePersonalizationDownloadProcessingJob;
36 import com.android.ondevicepersonalization.services.statsd.joblogging.OdpJobServiceLogger;
37 
38 import com.google.android.libraries.mobiledatadownload.tracing.PropagatedFutures;
39 import com.google.common.util.concurrent.FutureCallback;
40 import com.google.common.util.concurrent.Futures;
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 class MddJobService extends JobService {
48     private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
49     private static final String TAG = "MddJobService";
50 
51     private final Injector mInjector;
52 
MddJobService()53     public MddJobService() {
54         mInjector = new Injector();
55     }
56 
57     @VisibleForTesting
MddJobService(Injector injector)58     public MddJobService(Injector injector) {
59         mInjector = injector;
60     }
61 
62     static class Injector {
getBackgroundExecutor()63         ListeningExecutorService getBackgroundExecutor() {
64             return OnDevicePersonalizationExecutors.getBackgroundExecutor();
65         }
66 
getFlags()67         Flags getFlags() {
68             return FlagsFactory.getFlags();
69         }
70     }
71 
72     @Override
onStartJob(JobParameters params)73     public boolean onStartJob(JobParameters params) {
74         sLogger.d(TAG + ": onStartJob()");
75         OdpJobServiceLogger.getInstance(this).recordOnStartJob(params.getJobId());
76 
77         if (mInjector.getFlags().getGlobalKillSwitch()) {
78             sLogger.d(TAG + ": GlobalKillSwitch enabled, finishing job.");
79             return cancelAndFinishJob(params,
80                     AD_SERVICES_BACKGROUND_JOBS_EXECUTION_REPORTED__EXECUTION_RESULT_CODE__SKIP_FOR_KILL_SWITCH_ON);
81         }
82 
83         // Reschedule jobs with SPE if it's enabled.
84         // Note: scheduled jobs by this MddJobService will be cancelled for the same job ID
85         if (mInjector.getFlags().getSpeOnMddJobEnabled()) {
86             sLogger.d("SPE is enabled. Reschedule MddJobService with MddJob.");
87             MddTaskScheduler.schedule(/* context */ this, params.getExtras());
88             return false;
89         }
90 
91         // Run privacy status checks in the background
92         runPrivacyStatusChecksInBackgroundAndExecute(params);
93         return true;
94     }
95 
runPrivacyStatusChecksInBackgroundAndExecute(final JobParameters params)96     private void runPrivacyStatusChecksInBackgroundAndExecute(final JobParameters params) {
97         OnDevicePersonalizationExecutors.getHighPriorityBackgroundExecutor().execute(() -> {
98             if (UserPrivacyStatus.getInstance().isProtectedAudienceAndMeasurementBothDisabled()) {
99                 // User control is revoked; handle this case
100                 sLogger.d(TAG + ": User control is not given for all ODP services.");
101                 OdpJobServiceLogger.getInstance(MddJobService.this)
102                         .recordJobSkipped(params.getJobId(),
103                                 AD_SERVICES_BACKGROUND_JOBS_EXECUTION_REPORTED__EXECUTION_RESULT_CODE__SKIP_FOR_PERSONALIZATION_NOT_ENABLED);
104                 jobFinished(params, false);
105             } else {
106                 // User control is given; handle the MDD task
107                 ListenableFuture<Void> handleTaskFuture =
108                         PropagatedFutures.submitAsync(
109                                 () -> MobileDataDownloadFactory.getMdd(this)
110                                         .handleTask(getMddTaskTag(params.getExtras())),
111                                 mInjector.getBackgroundExecutor());
112 
113                 Futures.addCallback(
114                         handleTaskFuture,
115                         new FutureCallback<Void>() {
116                             @Override
117                             public void onSuccess(Void result) {
118                                 handleSuccess(params);
119                             }
120 
121                             @Override
122                             public void onFailure(Throwable t) {
123                                 handleFailure(params, t);
124                             }
125                         },
126                         mInjector.getBackgroundExecutor());
127             }
128         });
129     }
130 
handleSuccess(JobParameters params)131     private void handleSuccess(JobParameters params) {
132         sLogger.d(TAG + ": MddJobService.MddHandleTask succeeded!");
133         if (WIFI_CHARGING_PERIODIC_TASK.equals(getMddTaskTag(params.getExtras()))) {
134             OnDevicePersonalizationDownloadProcessingJob.schedule(/* context */ this);
135         }
136         recordJobFinished(params.getJobId(), true);
137         jobFinished(params, false);
138     }
139 
handleFailure(JobParameters params, Throwable throwable)140     private void handleFailure(JobParameters params, Throwable throwable) {
141         sLogger.e(TAG + ": Failed to handle JobService: " + params.getJobId(), throwable);
142         recordJobFinished(params.getJobId(), false);
143         jobFinished(params, false);
144     }
145 
recordJobFinished(int jobId, boolean isSuccessful)146     private void recordJobFinished(int jobId, boolean isSuccessful) {
147         boolean wantsReschedule = false;
148         OdpJobServiceLogger.getInstance(this)
149                 .recordJobFinished(jobId, isSuccessful, wantsReschedule);
150     }
151 
152     @Override
onStopJob(JobParameters params)153     public boolean onStopJob(JobParameters params) {
154         // Attempt to process any data downloaded before the worker was stopped.
155         if (WIFI_CHARGING_PERIODIC_TASK.equals(getMddTaskTag(params.getExtras()))) {
156             OnDevicePersonalizationDownloadProcessingJob.schedule(/* context */ this);
157         }
158         // Reschedule the job since it ended before finishing
159         boolean wantsReschedule = true;
160         OdpJobServiceLogger.getInstance(this)
161                 .recordOnStopJob(params, params.getJobId(), wantsReschedule);
162         return wantsReschedule;
163     }
164 
cancelAndFinishJob(final JobParameters params, int skipReason)165     private boolean cancelAndFinishJob(final JobParameters params, int skipReason) {
166         JobScheduler jobScheduler = this.getSystemService(JobScheduler.class);
167         int jobId = params.getJobId();
168         if (jobScheduler != null) {
169             jobScheduler.cancel(jobId);
170         }
171         OdpJobServiceLogger.getInstance(this).recordJobSkipped(jobId, skipReason);
172         jobFinished(params, /* wantsReschedule = */ false);
173         return true;
174     }
175 }
176