/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.healthconnect; import static android.health.connect.Constants.DEFAULT_INT; import static com.android.server.healthconnect.HealthConnectDailyJobs.HC_DAILY_JOB; import static com.android.server.healthconnect.exportimport.ExportImportJobs.PERIODIC_EXPORT_JOB_NAME; import static com.android.server.healthconnect.migration.MigrationConstants.MIGRATION_COMPLETE_JOB_NAME; import static com.android.server.healthconnect.migration.MigrationConstants.MIGRATION_PAUSE_JOB_NAME; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobScheduler; import android.app.job.JobService; import android.health.connect.Constants; import android.util.Slog; import com.android.server.healthconnect.exportimport.ExportImportJobs; import com.android.server.healthconnect.exportimport.ExportManager; import com.android.server.healthconnect.migration.MigrationStateChangeJob; import java.time.Clock; import java.util.Objects; /** * Health Connect wrapper around JobService. * * @hide */ public class HealthConnectDailyService extends JobService { public static final String EXTRA_USER_ID = "user_id"; public static final String EXTRA_JOB_NAME_KEY = "job_name"; private static final String TAG = "HealthConnectDailyService"; @UserIdInt private static volatile int sCurrentUserId; /** * Routes the job to the right place based on the job name, after performing common checks., * *
Please handle exceptions for each task within the task. Do not crash the job as it might * result in failure of other tasks being triggered from the job. */ @Override public boolean onStartJob(@NonNull JobParameters params) { int userId = params.getExtras().getInt(EXTRA_USER_ID, /* defaultValue= */ DEFAULT_INT); String jobName = params.getExtras().getString(EXTRA_JOB_NAME_KEY); if (userId == DEFAULT_INT || userId != sCurrentUserId) { // This job is no longer valid, the service for this user should have been stopped. // Just ignore this request in case we still got the request. return false; } if (Objects.isNull(jobName)) { return false; } // This service executes each incoming job on a Handler running on the application's // main thread. This means that we must offload the execution logic to background executor. switch (jobName) { case HC_DAILY_JOB: HealthConnectThreadScheduler.scheduleInternalTask( () -> { HealthConnectDailyJobs.execute(getApplicationContext(), params); jobFinished(params, false); }); return true; case MIGRATION_COMPLETE_JOB_NAME: HealthConnectThreadScheduler.scheduleInternalTask( () -> { MigrationStateChangeJob.executeMigrationCompletionJob( getApplicationContext()); jobFinished(params, false); }); return true; case MIGRATION_PAUSE_JOB_NAME: HealthConnectThreadScheduler.scheduleInternalTask( () -> { MigrationStateChangeJob.executeMigrationPauseJob( getApplicationContext()); jobFinished(params, false); }); return true; case PERIODIC_EXPORT_JOB_NAME: HealthConnectThreadScheduler.scheduleInternalTask( () -> { boolean isExportSuccessful = ExportImportJobs.executePeriodicExportJob( new ExportManager( getApplicationContext(), Clock.systemUTC())); // If the export is not successful, reschedule the job. jobFinished(params, !isExportSuccessful); }); return true; default: Slog.w(TAG, "Job name " + jobName + " is not supported."); break; } return false; } /** Called when job needs to be stopped. Don't do anything here and let the job be killed. */ @Override public boolean onStopJob(@NonNull JobParameters params) { return false; } /** Start periodically scheduling this service for {@code userId}. */ public static void schedule( @NonNull JobScheduler jobScheduler, @UserIdInt int userId, @NonNull JobInfo jobInfo) { Objects.requireNonNull(jobScheduler); sCurrentUserId = userId; int result = jobScheduler.schedule(jobInfo); if (result != JobScheduler.RESULT_SUCCESS) { Slog.e( TAG, "Failed to schedule the job: " + jobInfo.getExtras().getString(EXTRA_JOB_NAME_KEY)); } else if (Constants.DEBUG) { Slog.d( TAG, "Scheduled a job successfully: " + jobInfo.getExtras().getString(EXTRA_JOB_NAME_KEY)); } } }