Home | History | Annotate | Download | only in scheduling
      1 /*
      2  * Copyright (C) 2017 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.voicemail.impl.scheduling;
     18 
     19 import android.content.Context;
     20 import android.os.Bundle;
     21 import android.support.annotation.NonNull;
     22 import android.support.annotation.Nullable;
     23 import com.android.voicemail.impl.Assert;
     24 import com.android.voicemail.impl.VvmLog;
     25 import com.android.voicemail.impl.scheduling.Task.TaskId;
     26 import java.util.ArrayDeque;
     27 import java.util.ArrayList;
     28 import java.util.Iterator;
     29 import java.util.List;
     30 import java.util.Queue;
     31 
     32 /**
     33  * A queue that manages priority and duplication of {@link Task}. A task is identified by a {@link
     34  * TaskId}, which consists of an integer representing the operation the task, and a {@link
     35  * android.telecom.PhoneAccountHandle} representing which SIM it is operated on.
     36  */
     37 class TaskQueue implements Iterable<Task> {
     38 
     39   private final Queue<Task> queue = new ArrayDeque<>();
     40 
     41   public List<Bundle> toBundles() {
     42     List<Bundle> result = new ArrayList<>(queue.size());
     43     for (Task task : queue) {
     44       result.add(Tasks.toBundle(task));
     45     }
     46     return result;
     47   }
     48 
     49   public void fromBundles(Context context, List<Bundle> pendingTasks) {
     50     Assert.isTrue(queue.isEmpty());
     51     for (Bundle pendingTask : pendingTasks) {
     52       Task task = Tasks.createTask(context, pendingTask);
     53       task.onRestore(pendingTask);
     54       add(task);
     55     }
     56   }
     57 
     58   /**
     59    * Add a new task to the queue. A new task with a TaskId collision will be discarded, and {@link
     60    * Task#onDuplicatedTaskAdded(Task)} will be called on the existing task.
     61    *
     62    * @return {@code true} if the task is added, or {@code false} if the task is discarded due to
     63    *     collision.
     64    */
     65   public boolean add(Task task) {
     66     if (task.getId().id == Task.TASK_INVALID) {
     67       throw new AssertionError("Task id was not set to a valid value before adding.");
     68     }
     69     if (task.getId().id != Task.TASK_ALLOW_DUPLICATES) {
     70       Task oldTask = getTask(task.getId());
     71       if (oldTask != null) {
     72         oldTask.onDuplicatedTaskAdded(task);
     73         VvmLog.i("TaskQueue.add", "duplicated task added");
     74         return false;
     75       }
     76     }
     77     queue.add(task);
     78     return true;
     79   }
     80 
     81   public void remove(Task task) {
     82     queue.remove(task);
     83   }
     84 
     85   public Task getTask(TaskId id) {
     86     Assert.isMainThread();
     87     for (Task task : queue) {
     88       if (task.getId().equals(id)) {
     89         return task;
     90       }
     91     }
     92     return null;
     93   }
     94 
     95   /**
     96    * Packed return value of {@link #getNextTask(long)}. If a runnable task is found {@link
     97    * #minimalWaitTimeMillis} will be {@code null}. If no tasks is runnable {@link #task} will be
     98    * {@code null}, and {@link #minimalWaitTimeMillis} will contain the time to wait. If there are no
     99    * tasks at all both will be {@code null}.
    100    */
    101   static final class NextTask {
    102     @Nullable final Task task;
    103     @Nullable final Long minimalWaitTimeMillis;
    104 
    105     NextTask(@Nullable Task task, @Nullable Long minimalWaitTimeMillis) {
    106       this.task = task;
    107       this.minimalWaitTimeMillis = minimalWaitTimeMillis;
    108     }
    109   }
    110 
    111   /**
    112    * The next task is the first task with {@link Task#getReadyInMilliSeconds()} return a value less
    113    * then {@code readyToleranceMillis}, in insertion order. If no task matches this criteria, the
    114    * minimal value of {@link Task#getReadyInMilliSeconds()} is returned instead. If there are no
    115    * tasks at all, the minimalWaitTimeMillis will also be null.
    116    */
    117   @NonNull
    118   NextTask getNextTask(long readyToleranceMillis) {
    119     Long minimalWaitTime = null;
    120     for (Task task : queue) {
    121       long waitTime = task.getReadyInMilliSeconds();
    122       if (waitTime < readyToleranceMillis) {
    123         return new NextTask(task, 0L);
    124       } else {
    125         if (minimalWaitTime == null || waitTime < minimalWaitTime) {
    126           minimalWaitTime = waitTime;
    127         }
    128       }
    129     }
    130     return new NextTask(null, minimalWaitTime);
    131   }
    132 
    133   public void clear() {
    134     queue.clear();
    135   }
    136 
    137   public int size() {
    138     return queue.size();
    139   }
    140 
    141   public boolean isEmpty() {
    142     return queue.isEmpty();
    143   }
    144 
    145   @Override
    146   public Iterator<Task> iterator() {
    147     return queue.iterator();
    148   }
    149 }
    150