• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.server.healthconnect;
18 
19 import static android.health.connect.Constants.DEFAULT_INT;
20 
21 import static com.android.server.healthconnect.HealthConnectDailyJobs.HC_DAILY_JOB;
22 import static com.android.server.healthconnect.migration.MigrationConstants.MIGRATION_COMPLETE_JOB_NAME;
23 import static com.android.server.healthconnect.migration.MigrationConstants.MIGRATION_PAUSE_JOB_NAME;
24 
25 import android.annotation.NonNull;
26 import android.annotation.UserIdInt;
27 import android.app.job.JobInfo;
28 import android.app.job.JobParameters;
29 import android.app.job.JobScheduler;
30 import android.app.job.JobService;
31 import android.health.connect.Constants;
32 import android.util.Slog;
33 
34 import com.android.server.healthconnect.migration.MigrationStateChangeJob;
35 
36 import java.util.Objects;
37 
38 /**
39  * A service that is run periodically and triggers other periodic tasks..
40  *
41  * @hide
42  */
43 public class HealthConnectDailyService extends JobService {
44     public static final String EXTRA_USER_ID = "user_id";
45     public static final String EXTRA_JOB_NAME_KEY = "job_name";
46     private static final String TAG = "HealthConnectDailyService";
47     @UserIdInt private static volatile int sCurrentUserId;
48 
49     /**
50      * Called everytime when the operation corresponding to this service is to be performed,
51      *
52      * <p>Please handle exceptions for each task within the task. Do not crash the job as it might
53      * result in failure of other tasks being triggered from the job.
54      */
55     @Override
onStartJob(@onNull JobParameters params)56     public boolean onStartJob(@NonNull JobParameters params) {
57         int userId = params.getExtras().getInt(EXTRA_USER_ID, /* defaultValue= */ DEFAULT_INT);
58         String jobName = params.getExtras().getString(EXTRA_JOB_NAME_KEY);
59         if (userId == DEFAULT_INT || userId != sCurrentUserId) {
60             // This job is no longer valid, the service for this user should have been stopped.
61             // Just ignore this request in case we still got the request.
62             return false;
63         }
64 
65         if (Objects.isNull(jobName)) {
66             return false;
67         }
68 
69         // This service executes each incoming job on a Handler running on the application's
70         // main thread. This means that we must offload the execution logic to background executor.
71         switch (jobName) {
72             case HC_DAILY_JOB -> {
73                 HealthConnectThreadScheduler.scheduleInternalTask(
74                         () -> {
75                             HealthConnectDailyJobs.execute(getApplicationContext(), params);
76                             jobFinished(params, false);
77                         });
78                 return true;
79             }
80             case MIGRATION_COMPLETE_JOB_NAME -> {
81                 HealthConnectThreadScheduler.scheduleInternalTask(
82                         () -> {
83                             MigrationStateChangeJob.executeMigrationCompletionJob(
84                                     getApplicationContext());
85                             jobFinished(params, false);
86                         });
87                 return true;
88             }
89             case MIGRATION_PAUSE_JOB_NAME -> {
90                 HealthConnectThreadScheduler.scheduleInternalTask(
91                         () -> {
92                             MigrationStateChangeJob.executeMigrationPauseJob(
93                                     getApplicationContext());
94                             jobFinished(params, false);
95                         });
96                 return true;
97             }
98             default -> {
99                 Slog.w(TAG, "Job name " + jobName + " is not supported.");
100             }
101         }
102         return false;
103     }
104 
105     /** Called when job needs to be stopped. Don't do anything here and let the job be killed. */
106     @Override
onStopJob(@onNull JobParameters params)107     public boolean onStopJob(@NonNull JobParameters params) {
108         return false;
109     }
110 
111     /** Start periodically scheduling this service for {@code userId}. */
schedule( @onNull JobScheduler jobScheduler, @UserIdInt int userId, @NonNull JobInfo jobInfo)112     public static void schedule(
113             @NonNull JobScheduler jobScheduler, @UserIdInt int userId, @NonNull JobInfo jobInfo) {
114         Objects.requireNonNull(jobScheduler);
115         sCurrentUserId = userId;
116 
117         int result = jobScheduler.schedule(jobInfo);
118         if (result != JobScheduler.RESULT_SUCCESS) {
119             Slog.e(
120                     TAG,
121                     "Failed to schedule the job: "
122                             + jobInfo.getExtras().getString(EXTRA_JOB_NAME_KEY));
123         } else if (Constants.DEBUG) {
124             Slog.d(
125                     TAG,
126                     "Scheduled a job successfully: "
127                             + jobInfo.getExtras().getString(EXTRA_JOB_NAME_KEY));
128         }
129     }
130 }
131