FileDocCategorySizeDatePackage
TimeController.javaAPI DocAndroid 5.1 API11683Thu Mar 12 22:22:42 GMT 2015com.android.server.job.controllers

TimeController

public class TimeController extends StateController
This class sets an alarm for the next expiring job, and determines whether a job's minimum delay has been satisfied.

Fields Summary
private static final String
TAG
private static final String
ACTION_JOB_EXPIRED
private static final String
ACTION_JOB_DELAY_EXPIRED
private final android.app.PendingIntent
mDeadlineExpiredAlarmIntent
Set an alarm for the next job expiry.
private final android.app.PendingIntent
mNextDelayExpiredAlarmIntent
Set an alarm for the next job delay expiry. This
private long
mNextJobExpiredElapsedMillis
private long
mNextDelayExpiredElapsedMillis
private android.app.AlarmManager
mAlarmService
private final List
mTrackedJobs
List of tracked jobs, sorted asc. by deadline
private static TimeController
mSingleton
Singleton.
private final android.content.BroadcastReceiver
mAlarmExpiredReceiver
Constructors Summary
private TimeController(com.android.server.job.StateChangedListener stateChangedListener, android.content.Context context)

        super(stateChangedListener, context);
        mDeadlineExpiredAlarmIntent =
                PendingIntent.getBroadcast(mContext, 0 /* ignored */,
                        new Intent(ACTION_JOB_EXPIRED), 0);
        mNextDelayExpiredAlarmIntent =
                PendingIntent.getBroadcast(mContext, 0 /* ignored */,
                        new Intent(ACTION_JOB_DELAY_EXPIRED), 0);
        mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
        mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;

        // Register BR for these intents.
        IntentFilter intentFilter = new IntentFilter(ACTION_JOB_EXPIRED);
        intentFilter.addAction(ACTION_JOB_DELAY_EXPIRED);
        mContext.registerReceiver(mAlarmExpiredReceiver, intentFilter);
    
Methods Summary
private booleancanStopTrackingJob(JobStatus job)
Determines whether this controller can stop tracking the given job. The controller is no longer interested in a job once its time constraint is satisfied, and the job's deadline is fulfilled - unlike other controllers a time constraint can't toggle back and forth.

        return (!job.hasTimingDelayConstraint() ||
                job.timeDelayConstraintSatisfied.get()) &&
                (!job.hasDeadlineConstraint() ||
                        job.deadlineConstraintSatisfied.get());
    
private synchronized voidcheckExpiredDeadlinesAndResetAlarm()
Checks list of jobs for ones that have an expired deadline, sending them to the JobScheduler if so, removing them from this list, and updating the alarm for the next expiry time.

        long nextExpiryTime = Long.MAX_VALUE;
        final long nowElapsedMillis = SystemClock.elapsedRealtime();

        Iterator<JobStatus> it = mTrackedJobs.iterator();
        while (it.hasNext()) {
            JobStatus job = it.next();
            if (!job.hasDeadlineConstraint()) {
                continue;
            }
            final long jobDeadline = job.getLatestRunTimeElapsed();

            if (jobDeadline <= nowElapsedMillis) {
                job.deadlineConstraintSatisfied.set(true);
                mStateChangedListener.onRunJobNow(job);
                it.remove();
            } else {  // Sorted by expiry time, so take the next one and stop.
                nextExpiryTime = jobDeadline;
                break;
            }
        }
        setDeadlineExpiredAlarm(nextExpiryTime);
    
private synchronized voidcheckExpiredDelaysAndResetAlarm()
Handles alarm that notifies us that a job's delay has expired. Iterates through the list of tracked jobs and marks them as ready as appropriate.

        final long nowElapsedMillis = SystemClock.elapsedRealtime();
        long nextDelayTime = Long.MAX_VALUE;
        boolean ready = false;
        Iterator<JobStatus> it = mTrackedJobs.iterator();
        while (it.hasNext()) {
            final JobStatus job = it.next();
            if (!job.hasTimingDelayConstraint()) {
                continue;
            }
            final long jobDelayTime = job.getEarliestRunTime();
            if (jobDelayTime <= nowElapsedMillis) {
                job.timeDelayConstraintSatisfied.set(true);
                if (canStopTrackingJob(job)) {
                    it.remove();
                }
                if (job.isReady()) {
                    ready = true;
                }
            } else {  // Keep going through list to get next delay time.
                if (nextDelayTime > jobDelayTime) {
                    nextDelayTime = jobDelayTime;
                }
            }
        }
        if (ready) {
            mStateChangedListener.onControllerStateChanged();
        }
        setDelayExpiredAlarm(nextDelayTime);
    
public voiddumpControllerState(java.io.PrintWriter pw)


    
        
        final long nowElapsed = SystemClock.elapsedRealtime();
        pw.println("Alarms (" + SystemClock.elapsedRealtime() + ")");
        pw.println(
                "Next delay alarm in " + (mNextDelayExpiredElapsedMillis - nowElapsed)/1000 + "s");
        pw.println("Next deadline alarm in " + (mNextJobExpiredElapsedMillis - nowElapsed)/1000
                + "s");
        pw.println("Tracking:");
        for (JobStatus ts : mTrackedJobs) {
            pw.println(String.valueOf(ts.hashCode()).substring(0, 3) + ".."
                    + ": (" + (ts.hasTimingDelayConstraint() ? ts.getEarliestRunTime() : "N/A")
                    + ", " + (ts.hasDeadlineConstraint() ?ts.getLatestRunTimeElapsed() : "N/A")
                    + ")");
        }
    
private voidensureAlarmService()

        if (mAlarmService == null) {
            mAlarmService = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
        }
    
public static synchronized com.android.server.job.controllers.TimeControllerget(com.android.server.job.JobSchedulerService jms)


          
        if (mSingleton == null) {
            mSingleton = new TimeController(jms, jms.getContext());
        }
        return mSingleton;
    
private longmaybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis)

        final long earliestWakeupTimeElapsed = SystemClock.elapsedRealtime();
        if (proposedAlarmTimeElapsedMillis < earliestWakeupTimeElapsed) {
            return earliestWakeupTimeElapsed;
        }
        return proposedAlarmTimeElapsedMillis;
    
public synchronized voidmaybeStartTrackingJob(JobStatus job)
Check if the job has a timing constraint, and if so determine where to insert it in our list.

        if (job.hasTimingDelayConstraint() || job.hasDeadlineConstraint()) {
            maybeStopTrackingJob(job);
            ListIterator<JobStatus> it = mTrackedJobs.listIterator(mTrackedJobs.size());
            while (it.hasPrevious()) {
                JobStatus ts = it.previous();
                if (ts.getLatestRunTimeElapsed() < job.getLatestRunTimeElapsed()) {
                    // Insert
                    break;
                }
            }
            it.add(job);
            maybeUpdateAlarms(
                    job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE,
                    job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE);
        }
    
public synchronized voidmaybeStopTrackingJob(JobStatus job)
When we stop tracking a job, we only need to update our alarms if the job we're no longer tracking was the one our alarms were based off of. Really an == comparison should be enough, but why play with fate? We'll do <=.

        if (mTrackedJobs.remove(job)) {
            checkExpiredDelaysAndResetAlarm();
            checkExpiredDeadlinesAndResetAlarm();
        }
    
private voidmaybeUpdateAlarms(long delayExpiredElapsed, long deadlineExpiredElapsed)

        if (delayExpiredElapsed < mNextDelayExpiredElapsedMillis) {
            setDelayExpiredAlarm(delayExpiredElapsed);
        }
        if (deadlineExpiredElapsed < mNextJobExpiredElapsedMillis) {
            setDeadlineExpiredAlarm(deadlineExpiredElapsed);
        }
    
private voidsetDeadlineExpiredAlarm(long alarmTimeElapsedMillis)
Set an alarm with the {@link android.app.AlarmManager} for the next time at which a job's deadline will expire. This alarm will wake up the phone.

        alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
        mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis;
        updateAlarmWithPendingIntent(mDeadlineExpiredAlarmIntent, mNextJobExpiredElapsedMillis);
    
private voidsetDelayExpiredAlarm(long alarmTimeElapsedMillis)
Set an alarm with the {@link android.app.AlarmManager} for the next time at which a job's delay will expire. This alarm will not wake up the phone.

        alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
        mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis;
        updateAlarmWithPendingIntent(mNextDelayExpiredAlarmIntent, mNextDelayExpiredElapsedMillis);
    
private voidupdateAlarmWithPendingIntent(android.app.PendingIntent pi, long alarmTimeElapsed)

        ensureAlarmService();
        if (alarmTimeElapsed == Long.MAX_VALUE) {
            mAlarmService.cancel(pi);
        } else {
            if (DEBUG) {
                Slog.d(TAG, "Setting " + pi.getIntent().getAction() + " for: " + alarmTimeElapsed);
            }
            mAlarmService.set(AlarmManager.ELAPSED_REALTIME, alarmTimeElapsed, pi);
        }