Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a2a29b0c authored by Hung-ying Tyan's avatar Hung-ying Tyan
Browse files

Fix order of setting/saving state in VpnService.

and also refactor code making sure a thread won't grab two locks (which
may cause deadlocks in some corner cases).
parent e6034f6c
Loading
Loading
Loading
Loading
+42 −45
Original line number Diff line number Diff line
@@ -133,11 +133,7 @@ abstract class VpnService<E extends VpnProfile> implements Serializable {

        if (VpnState.CONNECTED.equals(mState)) {
            Log.i("VpnService", "     recovered: " + mProfile.getName());
            new Thread(new Runnable() {
                public void run() {
                    enterConnectivityLoop();
                }
            }).start();
            startConnectivityMonitor();
        }
    }

@@ -213,18 +209,20 @@ abstract class VpnService<E extends VpnProfile> implements Serializable {
                    SystemProperties.get(VPN_STATUS))) {
                onConnected();
                return;
            } else if (mDaemonHelper.anySocketError()) {
            } else {
                int err = mDaemonHelper.getSocketError();
                if (err != 0) {
                    onError(err);
                    return;
                }
            }
            sleep(500); // 0.5 second
        }

        synchronized (VpnService.this) {
        if (mState == VpnState.CONNECTING) {
            onError(new IOException("Connecting timed out"));
        }
    }
    }

    private synchronized void onConnected() throws IOException {
        if (DBG) Log.d(TAG, "onConnected()");
@@ -235,13 +233,15 @@ abstract class VpnService<E extends VpnProfile> implements Serializable {

        mStartTime = System.currentTimeMillis();

        // set DNS after saving the states in case the process gets killed
        // before states are saved
        // Correct order to make sure VpnService doesn't break when killed:
        // (1) set state to CONNECTED
        // (2) save states
        // (3) set DNS
        setState(VpnState.CONNECTED);
        saveSelf();
        setVpnDns();
        setState(VpnState.CONNECTED);

        enterConnectivityLoop();
        startConnectivityMonitor();
    }

    private void saveSelf() throws IOException {
@@ -340,12 +340,15 @@ abstract class VpnService<E extends VpnProfile> implements Serializable {
        }
    }

    private void enterConnectivityLoop() {
    private void startConnectivityMonitor() {
        new Thread(new Runnable() {
            public void run() {
                Log.i(TAG, "VPN connectivity monitor running");
                try {
                    for (;;) {
                        synchronized (VpnService.this) {
                    if (mState != VpnState.CONNECTED || !checkConnectivity()) {
                            if ((mState != VpnState.CONNECTED)
                                || !checkConnectivity()) {
                                break;
                            }
                            mNotification.update();
@@ -358,6 +361,8 @@ abstract class VpnService<E extends VpnProfile> implements Serializable {
                }
                Log.i(TAG, "VPN connectivity monitor stopped");
            }
        }).start();
    }

    private void saveLocalIpAndInterface(String serverIp) throws IOException {
        DatagramSocket s = new DatagramSocket();
@@ -432,12 +437,8 @@ abstract class VpnService<E extends VpnProfile> implements Serializable {
        }

        synchronized void stopAll() {
            if (mDaemonList.isEmpty()) {
                onFinalCleanUp();
            } else {
            for (DaemonProxy s : mDaemonList) s.stop();
        }
        }

        synchronized void closeSockets() {
            for (DaemonProxy s : mDaemonList) s.closeControlSocket();
@@ -461,30 +462,26 @@ abstract class VpnService<E extends VpnProfile> implements Serializable {
            }
        }

        synchronized boolean anySocketError() {
        synchronized int getSocketError() {
            for (DaemonProxy s : mDaemonList) {
                switch (getResultFromSocket(s)) {
                    case 0:
                        continue;
                        return 0;

                    case AUTH_ERROR_CODE:
                        onError(VpnManager.VPN_ERROR_AUTH);
                        return true;
                        return VpnManager.VPN_ERROR_AUTH;

                    case CHALLENGE_ERROR_CODE:
                        onError(VpnManager.VPN_ERROR_CHALLENGE);
                        return true;
                        return VpnManager.VPN_ERROR_CHALLENGE;

                    case REMOTE_HUNG_UP_ERROR_CODE:
                        onError(VpnManager.VPN_ERROR_REMOTE_HUNG_UP);
                        return true;
                        return VpnManager.VPN_ERROR_REMOTE_HUNG_UP;

                    default:
                        onError(VpnManager.VPN_ERROR_CONNECTION_FAILED);
                        return true;
                        return VpnManager.VPN_ERROR_CONNECTION_FAILED;
                }
            }
            return false;
            return 0;
        }
    }