1 /*
2  * Copyright (C) 2019 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.backup.encryption.testing;
18 
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.concurrent.AbstractExecutorService;
22 import java.util.concurrent.TimeUnit;
23 
24 /**
25  * ExecutorService which needs to be stepped through the jobs in its' queue.
26  *
27  * <p>This is a deliberately simple implementation because it's only used in testing. The queued
28  * jobs are run on the main thread to eliminate any race condition bugs.
29  */
30 public class QueuingNonAutomaticExecutorService extends AbstractExecutorService {
31 
32     private List<Runnable> mWaitingJobs = new ArrayList<>();
33     private int mWaitingJobCount = 0;
34 
35     @Override
shutdown()36     public void shutdown() {
37         mWaitingJobCount = mWaitingJobs.size();
38         mWaitingJobs = null; // This will force an error if jobs are submitted after shutdown
39     }
40 
41     @Override
shutdownNow()42     public List<Runnable> shutdownNow() {
43         List<Runnable> queuedJobs = mWaitingJobs;
44         shutdown();
45         return queuedJobs;
46     }
47 
48     @Override
isShutdown()49     public boolean isShutdown() {
50         return mWaitingJobs == null;
51     }
52 
53     @Override
isTerminated()54     public boolean isTerminated() {
55         return mWaitingJobs == null && mWaitingJobCount == 0;
56     }
57 
58     @Override
awaitTermination(long timeout, TimeUnit unit)59     public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
60         long expiry = System.currentTimeMillis() + unit.toMillis(timeout);
61         for (Runnable job : mWaitingJobs) {
62             if (System.currentTimeMillis() > expiry) {
63                 return false;
64             }
65 
66             job.run();
67         }
68         return true;
69     }
70 
71     @Override
execute(Runnable command)72     public void execute(Runnable command) {
73         mWaitingJobs.add(command);
74     }
75 
runNext()76     public void runNext() {
77         if (mWaitingJobs.isEmpty()) {
78             throw new IllegalStateException("Attempted to run jobs on an empty paused executor");
79         }
80 
81         mWaitingJobs.remove(0).run();
82     }
83 }
84