• 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.devicelockcontroller.services;
18 
19 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
20 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
21 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
22 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
23 
24 import static com.android.devicelockcontroller.common.DeviceLockConstants.SETUP_WIZARD_TIMEOUT_JOB_ID;
25 import static com.android.devicelockcontroller.policy.ProvisionStateController.ProvisionEvent.PROVISION_READY;
26 import static com.android.devicelockcontroller.policy.ProvisionStateController.ProvisionState.UNPROVISIONED;
27 
28 import android.app.job.JobInfo;
29 import android.app.job.JobParameters;
30 import android.app.job.JobScheduler;
31 import android.app.job.JobService;
32 import android.content.ComponentName;
33 import android.content.Context;
34 import android.net.NetworkRequest;
35 import android.provider.Settings;
36 
37 import com.android.devicelockcontroller.policy.PolicyObjectsProvider;
38 import com.android.devicelockcontroller.policy.ProvisionStateController;
39 import com.android.devicelockcontroller.storage.GlobalParametersClient;
40 import com.android.devicelockcontroller.storage.UserParameters;
41 import com.android.devicelockcontroller.util.LogUtil;
42 
43 import com.google.common.annotations.VisibleForTesting;
44 import com.google.common.util.concurrent.FutureCallback;
45 import com.google.common.util.concurrent.Futures;
46 import com.google.common.util.concurrent.ListenableFuture;
47 import com.google.common.util.concurrent.ListeningExecutorService;
48 import com.google.common.util.concurrent.MoreExecutors;
49 
50 import java.util.concurrent.Executors;
51 import java.util.concurrent.TimeUnit;
52 
53 public final class SetupWizardCompletionTimeoutJobService extends JobService {
54     private static final String TAG = "SetupWizardCompletionTimeoutJobService";
55     private static final long TIMEOUT_MINUTES = 60;
56 
57     private final ListeningExecutorService mListeningExecutorService =
58             MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
59     private final Context mContext;
60 
61     @VisibleForTesting
62     ListenableFuture<Void> mFuture;
63 
64     /**
65      * Create an instance of the job.
66      */
SetupWizardCompletionTimeoutJobService()67     public SetupWizardCompletionTimeoutJobService() {
68         super();
69         mContext = this;
70     }
71 
72     @VisibleForTesting
SetupWizardCompletionTimeoutJobService(Context context)73     SetupWizardCompletionTimeoutJobService(Context context) {
74         super();
75         mContext = context;
76     }
77 
78     @Override
onStartJob(JobParameters params)79     public boolean onStartJob(JobParameters params) {
80         LogUtil.i(TAG, "Starting job");
81 
82         // If SUW is already finished, there's nothing left to do.
83         if (isUserSetupComplete()) {
84             return false;
85         }
86 
87         // SUW did not finished in the allotted time. If the device is still unprovisioned
88         // and provisioning information is ready, start the provisioning flow.
89         Context appContext = mContext.getApplicationContext();
90         PolicyObjectsProvider policyObjects =
91                 (PolicyObjectsProvider) appContext;
92         ProvisionStateController provisionStateController =
93                 policyObjects.getProvisionStateController();
94 
95         mFuture = Futures.transformAsync(provisionStateController.getState(),
96                 state -> {
97                     UserParameters.setSetupWizardTimedOut(appContext);
98 
99                     if (state != UNPROVISIONED) {
100                         return Futures.immediateVoidFuture();
101                     }
102 
103                     GlobalParametersClient globalParametersClient =
104                             GlobalParametersClient.getInstance();
105                     return Futures.transformAsync(globalParametersClient.isProvisionReady(),
106                         isReady -> {
107                             if (isReady) {
108                                 LogUtil.i(TAG, "Starting provisioning flow since "
109                                         + "SUW did not complete in " + TIMEOUT_MINUTES
110                                         + " minutes");
111                                 return Futures.transform(provisionStateController
112                                         .setNextStateForEvent(PROVISION_READY),
113                                         unused -> null,
114                                         mListeningExecutorService);
115                             }
116                             return Futures.immediateVoidFuture();
117                         }, mListeningExecutorService);
118                 }, mListeningExecutorService);
119 
120         Futures.addCallback(mFuture, new FutureCallback<>() {
121             @Override
122             public void onSuccess(Void result) {
123                 LogUtil.i(TAG, "Job completed");
124 
125                 jobFinished(params, /* wantsReschedule= */ false);
126             }
127 
128             @Override
129             public void onFailure(Throwable t) {
130                 LogUtil.e(TAG, "Job failed", t);
131 
132                 jobFinished(params, /* wantsReschedule= */ true);
133             }
134         }, mListeningExecutorService);
135 
136         return true;
137     }
138 
139     @Override
onStopJob(JobParameters params)140     public boolean onStopJob(JobParameters params) {
141         LogUtil.i(TAG, "Stopping job");
142 
143         if (mFuture != null) {
144             mFuture.cancel(true);
145         }
146 
147         return true;
148     }
149 
150     /**
151      * Schedule a job that starts the provisioning flow in case SetupWizard does not complete
152      * in the allotted time.
153      */
scheduleSetupWizardCompletionTimeoutJob(Context context)154     public static void scheduleSetupWizardCompletionTimeoutJob(Context context) {
155         JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
156 
157         if (jobScheduler.getPendingJob(SETUP_WIZARD_TIMEOUT_JOB_ID) != null) {
158             LogUtil.w(TAG, "Job already scheduled");
159 
160             return;
161         }
162 
163         ComponentName componentName =
164                 new ComponentName(context, SetupWizardCompletionTimeoutJobService.class);
165         long delayMillis = TimeUnit.MINUTES.toMillis(TIMEOUT_MINUTES);
166 
167         NetworkRequest request = new NetworkRequest.Builder()
168                 .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
169                 .addCapability(NET_CAPABILITY_TRUSTED)
170                 .addCapability(NET_CAPABILITY_INTERNET)
171                 .addCapability(NET_CAPABILITY_NOT_VPN)
172                 .build();
173 
174         JobInfo jobInfo = new JobInfo.Builder(SETUP_WIZARD_TIMEOUT_JOB_ID, componentName)
175                 .setMinimumLatency(delayMillis)
176                 .setRequiredNetwork(request)
177                 .build();
178 
179         int schedulingResult = jobScheduler.schedule(jobInfo);
180 
181         if (schedulingResult == JobScheduler.RESULT_SUCCESS) {
182             LogUtil.i(TAG, "Job scheduled");
183         } else {
184             LogUtil.e(TAG, "Failed to schedule job");
185         }
186     }
187 
188     /**
189      * Cancel the job that starts the provisioning flow if SetupWizard does not complete in
190      * the allotted time.
191      */
cancelSetupWizardCompletionTimeoutJob(Context context)192     public static void cancelSetupWizardCompletionTimeoutJob(Context context) {
193         JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
194 
195         jobScheduler.cancel(SETUP_WIZARD_TIMEOUT_JOB_ID);
196 
197         LogUtil.i(TAG, "Job cancelled");
198     }
199 
isUserSetupComplete()200     private boolean isUserSetupComplete() {
201         return Settings.Secure.getInt(
202                 mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
203     }
204 }
205