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