summaryrefslogtreecommitdiffstats
path: root/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java
blob: 50fbf4b87822eabd74297066a00af54787464661 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/*
 * Copyright (C) 2009, 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.vpn;

import android.os.ConditionVariable;

import java.io.IOException;

/**
 * A proxy class that spawns a process to accomplish a certain task.
 */
public abstract class ProcessProxy {
    /**
     * Defines the interface to call back when the process is finished or an
     * error occurs during execution.
     */
    public static interface Callback {
        /**
         * Called when the process is finished.
         * @param proxy the proxy that hosts the process
         */
        void done(ProcessProxy proxy);

        /**
         * Called when some error occurs.
         * @param proxy the proxy that hosts the process
         */
        void error(ProcessProxy proxy, Throwable error);
    }

    protected enum ProcessState {
        STOPPED, STARTING, RUNNING, STOPPING, ERROR
    }

    private ProcessState mState = ProcessState.STOPPED;
    private ConditionVariable mLock = new ConditionVariable();
    private Thread mThread;

    /**
     * Returns the name of the process.
     */
    public abstract String getName();

    /**
     * Starts the process with a callback.
     * @param callback the callback to get notified when the process is finished
     *      or an error occurs during execution
     * @throws IOException when the process is already running or failed to
     *      start
     */
    public synchronized void start(final Callback callback) throws IOException {
        if (!isStopped()) {
            throw new IOException("Process is already running: " + this);
        }
        mLock.close();
        setState(ProcessState.STARTING);
        Thread thread = new Thread(new Runnable() {
            public void run() {
                try {
                    performTask();
                    setState(ProcessState.STOPPED);
                    mLock.open();
                    if (callback != null) callback.done(ProcessProxy.this);
                } catch (Throwable e) {
                    setState(ProcessState.ERROR);
                    if (callback != null) callback.error(ProcessProxy.this, e);
                } finally {
                    mThread = null;
                }
            }
        });
        thread.setPriority(Thread.MIN_PRIORITY);
        thread.start();
        mThread = thread;
        if (!waitUntilRunning()) {
            throw new IOException("Failed to start the process: " + this);
        }
    }

    /**
     * Starts the process.
     * @throws IOException when the process is already running or failed to
     *      start
     */
    public synchronized void start() throws IOException {
        start(null);
        if (!waitUntilDone()) {
            throw new IOException("Failed to complete the process: " + this);
        }
    }

    /**
     * Returns the thread that hosts the process.
     */
    public Thread getHostThread() {
        return mThread;
    }

    /**
     * Blocks the current thread until the hosted process is finished.
     *
     * @return true if the process is finished normally; false if an error
     *      occurs
     */
    public boolean waitUntilDone() {
        while (!mLock.block(1000)) {
            if (isStopped() || isInError()) break;
        }
        return isStopped();
    }

    /**
     * Blocks the current thread until the hosted process is running.
     *
     * @return true if the process is running normally; false if the process
     *      is in another state
     */
    private boolean waitUntilRunning() {
        for (;;) {
            if (!isStarting()) break;
        }
        return isRunning();
    }

    /**
     * Stops and destroys the process.
     */
    public abstract void stop();

    /**
     * Checks whether the process is finished.
     * @return true if the process is stopped
     */
    public boolean isStopped() {
        return (mState == ProcessState.STOPPED);
    }

    /**
     * Checks whether the process is being stopped.
     * @return true if the process is being stopped
     */
    public boolean isStopping() {
        return (mState == ProcessState.STOPPING);
    }

    /**
     * Checks whether the process is being started.
     * @return true if the process is being started
     */
    public boolean isStarting() {
        return (mState == ProcessState.STARTING);
    }

    /**
     * Checks whether the process is running.
     * @return true if the process is running
     */
    public boolean isRunning() {
        return (mState == ProcessState.RUNNING);
    }

    /**
     * Checks whether some error has occurred and the process is stopped.
     * @return true if some error has occurred and the process is stopped
     */
    public boolean isInError() {
        return (mState == ProcessState.ERROR);
    }

    /**
     * Performs the actual task. Subclasses must make sure that the method
     * is blocked until the task is done or an error occurs.
     */
    protected abstract void performTask()
            throws IOException, InterruptedException;

    /**
     * Sets the process state.
     * @param state the new state to be in
     */
    protected void setState(ProcessState state) {
        mState = state;
    }

    /**
     * Makes the current thread sleep for the specified time.
     * @param msec time to sleep in miliseconds
     */
    protected void sleep(int msec) {
        try {
            Thread.currentThread().sleep(msec);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}