<commit_before>package me.tatarka.support.server.job;

import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import android.os.SystemClock;
import android.support.v4.content.WakefulBroadcastReceiver;

import java.util.ArrayList;
import java.util.List;

import me.tatarka.support.internal.util.ArraySet;
import me.tatarka.support.job.JobInfo;
import me.tatarka.support.server.job.controllers.BootReceiver;
import me.tatarka.support.server.job.controllers.IdleReceiver;
import me.tatarka.support.server.job.controllers.JobStatus;
import me.tatarka.support.server.job.controllers.NetworkReceiver;
import me.tatarka.support.server.job.controllers.PowerReceiver;
import me.tatarka.support.server.job.controllers.ReceiverUtils;
import me.tatarka.support.server.job.controllers.TimeReceiver;

/**
 * @hide *
 */
public class JobServiceCompat extends IntentService {
    static final String TAG = "JobServiceCompat";

    private static final String EXTRA_MSG = "EXTRA_MSG";
    private static final String EXTRA_JOB = "EXTRA_JOB";
    private static final String EXTRA_JOB_ID = "EXTRA_JOB_ID";
    private static final String EXTRA_RELEASE_WAKE_LOCK = "EXTRA_RELEASE_WAKE_LOCK";

    private static final int MSG_SCHEDULE_JOB = 0;
    private static final int MSG_RESCHEDULE_JOB = 1;
    private static final int MSG_CANCEL_JOB = 2;
    private static final int MSG_CANCEL_ALL = 3;
    private static final int MSG_RUN_JOBS = 4;
    private static final int MSG_JOBS_FINISHED = 6;
    private static final int MSG_BOOT = 7;

    private static PowerManager.WakeLock WAKE_LOCK;

    public JobServiceCompat() {
        super("JobServiceCompat");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);

        if (WAKE_LOCK == null) {
            WAKE_LOCK = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "JobServiceCompat");
        }
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        int msg = intent.getIntExtra(EXTRA_MSG, -1);
        switch (msg) {
            case MSG_SCHEDULE_JOB: {
                JobInfo job = intent.getParcelableExtra(EXTRA_JOB);
                handleSchedule(job);
                break;
            }
            case MSG_RESCHEDULE_JOB: {
                int jobId = intent.getIntExtra(EXTRA_JOB_ID, 0);
                handelReschedule(jobId);
                break;
            }
            case MSG_CANCEL_JOB: {
                int jobId = intent.getIntExtra(EXTRA_JOB_ID, 0);
                handleCancelJob(jobId);
                break;
            }
            case MSG_CANCEL_ALL: {
                handleCancelAll();
                break;
            }
            case MSG_RUN_JOBS: {
                handleRunJobs();
                boolean releaseWakeLock = intent.getBooleanExtra(EXTRA_RELEASE_WAKE_LOCK, false);
                if (releaseWakeLock) {
                    WakefulBroadcastReceiver.completeWakefulIntent(intent);
                }
                break;
            }
            case MSG_BOOT: {
                handleBoot();
            }
            case MSG_JOBS_FINISHED: {
                handleJobsFinished();
            }
        }
    }

    private void handleSchedule(JobInfo job) {
        JobStatus jobStatus = new JobStatus(job);
        JobStore jobStore = JobStore.initAndGet(this);
        synchronized (jobStore) {
            JobStatus oldJobStats = jobStore.getJobByJobId(job.getId());
            jobStore.remove(oldJobStats);
            jobStore.add(jobStatus);
        }
        scheduleJob(jobStatus);
    }

    private void scheduleJob(JobStatus job) {
        TimeReceiver.setAlarmsForJob(this, job);

        if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()) {
            ReceiverUtils.enable(this, NetworkReceiver.class);
        }

        if (job.hasChargingConstraint()) {
            ReceiverUtils.enable(this, PowerReceiver.class);
        }

        if (job.hasIdleConstraint()) {
            ReceiverUtils.enable(this, IdleReceiver.class);
        }

        if (job.isPersisted()) {
            ReceiverUtils.enable(this, BootReceiver.class);
        }
    }

    private void handelReschedule(int jobId) {
        JobStore jobStore = JobStore.initAndGet(this);
        synchronized (jobStore) {
            JobStatus jobStatus = jobStore.getJobByJobId(jobId);
            jobStore.remove(jobStatus);
            JobStatus newJobStats = rescheduleJob(jobStatus);
            jobStore.add(newJobStats);
        }
    }

    private JobStatus rescheduleJob(JobStatus job) {
        if (job.hasIdleConstraint()) {
            // TODO: different reschedule policy
            throw new UnsupportedOperationException("rescheduling idle tasks is not yet implemented");
        }

        final long elapsedNowMillis = SystemClock.elapsedRealtime();
        final JobInfo jobInfo = job.getJob();

        final long initialBackoffMillis = jobInfo.getInitialBackoffMillis();
        final int backoffAttemps = job.getNumFailures() + 1;

        long delayMillis;
        switch (job.getJob().getBackoffPolicy()) {
            case JobInfo.BACKOFF_POLICY_LINEAR:
                delayMillis = initialBackoffMillis * backoffAttemps;
                break;
            default:
            case JobInfo.BACKOFF_POLICY_EXPONENTIAL:
                delayMillis = (long) Math.scalb(initialBackoffMillis, backoffAttemps - 1);
                break;<commit_msg>support rescheduling of idle jobs<commit_after>package me.tatarka.support.server.job;

import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import android.os.SystemClock;
import android.support.v4.content.WakefulBroadcastReceiver;

import java.util.ArrayList;
import java.util.List;

import me.tatarka.support.internal.util.ArraySet;
import me.tatarka.support.job.JobInfo;
import me.tatarka.support.server.job.controllers.BootReceiver;
import me.tatarka.support.server.job.controllers.IdleReceiver;
import me.tatarka.support.server.job.controllers.JobStatus;
import me.tatarka.support.server.job.controllers.NetworkReceiver;
import me.tatarka.support.server.job.controllers.PowerReceiver;
import me.tatarka.support.server.job.controllers.ReceiverUtils;
import me.tatarka.support.server.job.controllers.TimeReceiver;

/**
 * @hide *
 */
public class JobServiceCompat extends IntentService {
    static final String TAG = "JobServiceCompat";

    private static final String EXTRA_MSG = "EXTRA_MSG";
    private static final String EXTRA_JOB = "EXTRA_JOB";
    private static final String EXTRA_JOB_ID = "EXTRA_JOB_ID";
    private static final String EXTRA_RELEASE_WAKE_LOCK = "EXTRA_RELEASE_WAKE_LOCK";

    private static final int MSG_SCHEDULE_JOB = 0;
    private static final int MSG_RESCHEDULE_JOB = 1;
    private static final int MSG_CANCEL_JOB = 2;
    private static final int MSG_CANCEL_ALL = 3;
    private static final int MSG_RUN_JOBS = 4;
    private static final int MSG_JOBS_FINISHED = 6;
    private static final int MSG_BOOT = 7;

    private static PowerManager.WakeLock WAKE_LOCK;

    public JobServiceCompat() {
        super("JobServiceCompat");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);

        if (WAKE_LOCK == null) {
            WAKE_LOCK = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "JobServiceCompat");
        }
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        int msg = intent.getIntExtra(EXTRA_MSG, -1);
        switch (msg) {
            case MSG_SCHEDULE_JOB: {
                JobInfo job = intent.getParcelableExtra(EXTRA_JOB);
                handleSchedule(job);
                break;
            }
            case MSG_RESCHEDULE_JOB: {
                int jobId = intent.getIntExtra(EXTRA_JOB_ID, 0);
                handelReschedule(jobId);
                break;
            }
            case MSG_CANCEL_JOB: {
                int jobId = intent.getIntExtra(EXTRA_JOB_ID, 0);
                handleCancelJob(jobId);
                break;
            }
            case MSG_CANCEL_ALL: {
                handleCancelAll();
                break;
            }
            case MSG_RUN_JOBS: {
                handleRunJobs();
                boolean releaseWakeLock = intent.getBooleanExtra(EXTRA_RELEASE_WAKE_LOCK, false);
                if (releaseWakeLock) {
                    WakefulBroadcastReceiver.completeWakefulIntent(intent);
                }
                break;
            }
            case MSG_BOOT: {
                handleBoot();
            }
            case MSG_JOBS_FINISHED: {
                handleJobsFinished();
            }
        }
    }

    private void handleSchedule(JobInfo job) {
        JobStatus jobStatus = new JobStatus(job);
        JobStore jobStore = JobStore.initAndGet(this);
        synchronized (jobStore) {
            JobStatus oldJobStats = jobStore.getJobByJobId(job.getId());
            jobStore.remove(oldJobStats);
            jobStore.add(jobStatus);
        }
        scheduleJob(jobStatus);
    }

    private void scheduleJob(JobStatus job) {
        TimeReceiver.setAlarmsForJob(this, job);

        if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()) {
            ReceiverUtils.enable(this, NetworkReceiver.class);
        }

        if (job.hasChargingConstraint()) {
            ReceiverUtils.enable(this, PowerReceiver.class);
        }

        if (job.hasIdleConstraint()) {
            ReceiverUtils.enable(this, IdleReceiver.class);
        }

        if (job.isPersisted()) {
            ReceiverUtils.enable(this, BootReceiver.class);
        }
    }

    private void handelReschedule(int jobId) {
        JobStore jobStore = JobStore.initAndGet(this);
        synchronized (jobStore) {
            JobStatus jobStatus = jobStore.getJobByJobId(jobId);
            jobStore.remove(jobStatus);
            JobStatus newJobStats = rescheduleJob(jobStatus);
            jobStore.add(newJobStats);
        }
    }

    private JobStatus rescheduleJob(JobStatus job) {
        if (job.hasIdleConstraint()) {
            // Can just re-add the current job, it will be executed in the next idle window.
            return job;
        }

        final long elapsedNowMillis = SystemClock.elapsedRealtime();
        final JobInfo jobInfo = job.getJob();

        final long initialBackoffMillis = jobInfo.getInitialBackoffMillis();
        final int backoffAttemps = job.getNumFailures() + 1;

        long delayMillis;
        switch (job.getJob().getBackoffPolicy()) {
            case JobInfo.BACKOFF_POLICY_LINEAR:
                delayMillis = initialBackoffMillis * backoffAttemps;
                break;
            default:
            case JobInfo.BACKOFF_POLICY_EXPONENTIAL:
                delayMillis = (long) Math.scalb(initialBackoffMillis, backoffAttemps - 1);
                break;
{'fraction_non_alphanumeric': 0.08485393258426967, 'fraction_numerical': 0.0023370786516853934, 'mean_word_length': 2.3401380966676673, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3957345', 'n_tokens_mistral': 3299, 'n_tokens_neox': 3201, 'n_words': 615}