1ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh/*
2ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * Copyright (C) 2011 The Android Open Source Project
3ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh *
4ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * Licensed under the Apache License, Version 2.0 (the "License");
5ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * you may not use this file except in compliance with the License.
6ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * You may obtain a copy of the License at
7ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh *
8ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh *      http://www.apache.org/licenses/LICENSE-2.0
9ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh *
10ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * Unless required by applicable law or agreed to in writing, software
11ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * distributed under the License is distributed on an "AS IS" BASIS,
12ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * See the License for the specific language governing permissions and
14ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * limitations under the License.
15ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh */
16ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
17ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehpackage com.android.server.connectivity;
18ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
19ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.app.Notification;
20ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.app.NotificationManager;
21199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.ComponentName;
22ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.Context;
23ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.Intent;
24199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.ServiceConnection;
25ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.pm.ApplicationInfo;
26ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.pm.PackageManager;
27199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.pm.ResolveInfo;
28ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.res.Resources;
29ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.graphics.Bitmap;
30ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.graphics.Canvas;
31ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.graphics.drawable.Drawable;
32ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.net.INetworkManagementEventObserver;
3385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.net.LocalSocket;
3485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.net.LocalSocketAddress;
35ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.os.Binder;
36199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.os.IBinder;
37199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.os.Parcel;
38ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.os.ParcelFileDescriptor;
3985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.os.Process;
4085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.os.SystemClock;
4185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.os.SystemProperties;
42ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.util.Log;
43ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
44ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport com.android.internal.R;
452e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yehimport com.android.internal.net.LegacyVpnInfo;
4604ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yehimport com.android.internal.net.VpnConfig;
47ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport com.android.server.ConnectivityService.VpnCallback;
48ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
4997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yehimport java.io.File;
5097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yehimport java.io.FileInputStream;
5197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yehimport java.io.InputStream;
5285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport java.io.OutputStream;
5385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport java.nio.charset.Charsets;
5441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yehimport java.util.Arrays;
5585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
56ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh/**
57ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * @hide
58ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh */
59ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehpublic class Vpn extends INetworkManagementEventObserver.Stub {
60ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
61ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    private final static String TAG = "Vpn";
62ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
63199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    private final static String BIND_VPN_SERVICE =
64199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            android.Manifest.permission.BIND_VPN_SERVICE;
65199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
66ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    private final Context mContext;
67ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    private final VpnCallback mCallback;
68ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
69c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private String mPackage = VpnConfig.LEGACY_VPN;
70c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private String mInterface;
71199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    private Connection mConnection;
7285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    private LegacyVpnRunner mLegacyVpnRunner;
73ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
74ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    public Vpn(Context context, VpnCallback callback) {
75ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        mContext = context;
76ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        mCallback = callback;
77ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
78ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
79ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    /**
80100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * Prepare for a VPN application. This method is designed to solve
81100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * race conditions. It first compares the current prepared package
82100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * with {@code oldPackage}. If they are the same, the prepared
83100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * package is revoked and replaced with {@code newPackage}. If
84100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * {@code oldPackage} is {@code null}, the comparison is omitted.
85100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * If {@code newPackage} is the same package or {@code null}, the
86100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * revocation is omitted. This method returns {@code true} if the
87100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * operation is succeeded.
88e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     *
89100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * Legacy VPN is handled specially since it is not a real package.
90100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
91100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * it can be revoked by itself.
92100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     *
93100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @param oldPackage The package name of the old VPN application.
94100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @param newPackage The package name of the new VPN application.
95100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @return true if the operation is succeeded.
96ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
97100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh    public synchronized boolean prepare(String oldPackage, String newPackage) {
98100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Return false if the package does not match.
99c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        if (oldPackage != null && !oldPackage.equals(mPackage)) {
100100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh            return false;
101100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        }
102100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh
103100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Return true if we do not need to revoke.
104100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        if (newPackage == null ||
105c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                (newPackage.equals(mPackage) && !newPackage.equals(VpnConfig.LEGACY_VPN))) {
106100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh            return true;
107ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
108ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
109100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Only system user can revoke a package.
11041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
11141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            throw new SecurityException("Unauthorized Caller");
11241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        }
1137b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh
114ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        // Reset the interface and hide the notification.
115c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        if (mInterface != null) {
116c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            jniReset(mInterface);
1177b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            mCallback.restore();
118ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            hideNotification();
119c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            mInterface = null;
120ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
121ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
122fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        // Revoke the connection or stop LegacyVpnRunner.
123199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        if (mConnection != null) {
124199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            try {
125199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
126199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                        Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
127199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            } catch (Exception e) {
128199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                // ignore
129199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
130199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mContext.unbindService(mConnection);
131199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mConnection = null;
132e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh        } else if (mLegacyVpnRunner != null) {
13341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mLegacyVpnRunner.exit();
13441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mLegacyVpnRunner = null;
13541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        }
13641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
137c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
138c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        mPackage = newPackage;
139100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        return true;
140ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
141ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
142ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    /**
143fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh     * Protect a socket from routing changes by binding it to the given
144fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh     * interface. The socket is NOT closed by this method.
145fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh     *
146fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh     * @param socket The socket to be bound.
147fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh     * @param name The name of the interface.
148fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh     */
149fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    public void protect(ParcelFileDescriptor socket, String interfaze) throws Exception {
150fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        PackageManager pm = mContext.getPackageManager();
151fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        ApplicationInfo app = pm.getApplicationInfo(mPackage, 0);
152fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        if (Binder.getCallingUid() != app.uid) {
153fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh            throw new SecurityException("Unauthorized Caller");
154fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        }
155fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        jniProtect(socket.getFd(), interfaze);
156fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    }
157fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh
158fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    /**
159e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * Establish a VPN network and return the file descriptor of the VPN
160e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * interface. This methods returns {@code null} if the application is
161100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * revoked or not prepared.
162ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     *
163e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @param config The parameters to configure the network.
164e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @return The file descriptor of the VPN interface.
165ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
16604ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yeh    public synchronized ParcelFileDescriptor establish(VpnConfig config) {
167ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        // Check if the caller is already prepared.
168ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        PackageManager pm = mContext.getPackageManager();
169ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        ApplicationInfo app = null;
170ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        try {
171c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            app = pm.getApplicationInfo(mPackage, 0);
172ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        } catch (Exception e) {
1737b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            return null;
174ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
175ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        if (Binder.getCallingUid() != app.uid) {
1767b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            return null;
177ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
178ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
179fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        // Check if the service is properly declared.
180199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
181199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        intent.setClassName(mPackage, config.user);
182199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        ResolveInfo info = pm.resolveService(intent, 0);
183199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        if (info == null) {
184199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            throw new SecurityException("Cannot find " + config.user);
185199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
186199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
187199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
188199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
189fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh
190a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        // Load the label.
191a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        String label = app.loadLabel(pm).toString();
192a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh
193a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        // Load the icon and convert it into a bitmap.
194a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        Drawable icon = app.loadIcon(pm);
195a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        Bitmap bitmap = null;
196a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
197a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            int width = mContext.getResources().getDimensionPixelSize(
198a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh                    android.R.dimen.notification_large_icon_width);
199a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            int height = mContext.getResources().getDimensionPixelSize(
200a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh                    android.R.dimen.notification_large_icon_height);
201a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            icon.setBounds(0, 0, width, height);
202a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
2036311d0a079702b29984c0d31937345be105e1a5eDianne Hackborn            Canvas c = new Canvas(bitmap);
2046311d0a079702b29984c0d31937345be105e1a5eDianne Hackborn            icon.draw(c);
2056311d0a079702b29984c0d31937345be105e1a5eDianne Hackborn            c.setBitmap(null);
206a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        }
207a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh
208e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh        // Configure the interface. Abort if any of these steps fails.
20997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh        ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
210ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        try {
211c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            String interfaze = jniGetName(tun.getFd());
21297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            if (jniSetAddresses(interfaze, config.addresses) < 1) {
21397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                throw new IllegalArgumentException("At least one address must be specified");
21497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            }
21597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            if (config.routes != null) {
21697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                jniSetRoutes(interfaze, config.routes);
21797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            }
218199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            Connection connection = new Connection();
219199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            if (!mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
220199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                throw new IllegalStateException("Cannot bind " + config.user);
221199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
222199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            if (mConnection != null) {
223199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                mContext.unbindService(mConnection);
224199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
225c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            if (mInterface != null && !mInterface.equals(interfaze)) {
226c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                jniReset(mInterface);
227ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            }
228199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mConnection = connection;
229c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            mInterface = interfaze;
230ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        } catch (RuntimeException e) {
231ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            try {
232c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                tun.close();
233ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            } catch (Exception ex) {
234ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                // ignore
235ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            }
236ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            throw e;
237ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
238199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        Log.i(TAG, "Established by " + config.user + " on " + mInterface);
239ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
2408909b10175303bd5e2ca82b7ba12cd0017050ef3Chia-chi Yeh        // Fill more values.
241fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        config.user = mPackage;
242c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        config.interfaze = mInterface;
2438909b10175303bd5e2ca82b7ba12cd0017050ef3Chia-chi Yeh
244fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        // Override DNS servers and show the notification.
245fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        long identity = Binder.clearCallingIdentity();
246fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        mCallback.override(config.dnsServers, config.searchDomains);
247383e0524726d64302322abeba16d87faf66bae99Chia-chi Yeh        showNotification(config, label, bitmap);
248fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        Binder.restoreCallingIdentity(identity);
249c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        return tun;
250ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
251ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
252ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    // INetworkManagementEventObserver.Stub
253fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    @Override
254aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh    public void interfaceAdded(String interfaze) {
255f59c7d0f2ac8d489b6d8118543a57ea4a603eacfMike J. Chen    }
256f59c7d0f2ac8d489b6d8118543a57ea4a603eacfMike J. Chen
257f59c7d0f2ac8d489b6d8118543a57ea4a603eacfMike J. Chen    // INetworkManagementEventObserver.Stub
258fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    @Override
259aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh    public synchronized void interfaceStatusChanged(String interfaze, boolean up) {
260aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        if (!up && mLegacyVpnRunner != null) {
261aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh            mLegacyVpnRunner.check(interfaze);
262aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        }
263ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
264ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
265ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    // INetworkManagementEventObserver.Stub
266fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    @Override
267fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    public void interfaceLinkStateChanged(String interfaze, boolean up) {
268ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
269ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
270ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    // INetworkManagementEventObserver.Stub
271fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    @Override
272c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    public synchronized void interfaceRemoved(String interfaze) {
273c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
274fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh            long identity = Binder.clearCallingIdentity();
275ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            mCallback.restore();
276e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh            hideNotification();
277fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh            Binder.restoreCallingIdentity(identity);
278c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            mInterface = null;
279199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            if (mConnection != null) {
280199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                mContext.unbindService(mConnection);
281199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                mConnection = null;
2820c074e68437f1a705a8e73ac32e8e4dec370ec43Chia-chi Yeh            } else if (mLegacyVpnRunner != null) {
2830c074e68437f1a705a8e73ac32e8e4dec370ec43Chia-chi Yeh                mLegacyVpnRunner.exit();
2840c074e68437f1a705a8e73ac32e8e4dec370ec43Chia-chi Yeh                mLegacyVpnRunner = null;
285199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
286ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
287ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
288ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
289fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    // INetworkManagementEventObserver.Stub
290fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    @Override
291fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    public void limitReached(String limit, String interfaze) {
292fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    }
29312b933d0d9252decaae9fee2456bb1e1cd94c085JP Abgrall
294199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    private class Connection implements ServiceConnection {
295199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        private IBinder mService;
296199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
297199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        @Override
298199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        public void onServiceConnected(ComponentName name, IBinder service) {
299199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mService = service;
300199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
301199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
302199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        @Override
303199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        public void onServiceDisconnected(ComponentName name) {
304199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mService = null;
305199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
306199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    }
307199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
308a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh    private void showNotification(VpnConfig config, String label, Bitmap icon) {
309ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        NotificationManager nm = (NotificationManager)
310ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
311ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
312ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        if (nm != null) {
313a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            String title = (label == null) ? mContext.getString(R.string.vpn_title) :
314a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh                    mContext.getString(R.string.vpn_title_long, label);
31534e7813e962de99df9813014678ef5901227c5f1Chia-chi Yeh            String text = (config.session == null) ? mContext.getString(R.string.vpn_text) :
31634e7813e962de99df9813014678ef5901227c5f1Chia-chi Yeh                    mContext.getString(R.string.vpn_text_long, config.session);
3172e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            config.startTime = SystemClock.elapsedRealtime();
318a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh
319ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            Notification notification = new Notification.Builder(mContext)
320ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                    .setSmallIcon(R.drawable.vpn_connected)
321a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh                    .setLargeIcon(icon)
322a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh                    .setContentTitle(title)
323f8905fd13da0bfd6049daebc1cf4f8af286a04deChia-chi Yeh                    .setContentText(text)
3242e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    .setContentIntent(VpnConfig.getIntentForStatusPanel(mContext, config))
32550fe709995d1f126e96cafde133bc4777b31d4edChia-chi Yeh                    .setDefaults(0)
326ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                    .setOngoing(true)
327ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                    .getNotification();
328ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            nm.notify(R.drawable.vpn_connected, notification);
329ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
330ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
331ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
332ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    private void hideNotification() {
333ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        NotificationManager nm = (NotificationManager)
334ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
335ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
336ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        if (nm != null) {
337ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            nm.cancel(R.drawable.vpn_connected);
338ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
339ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
340ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
34197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh    private native int jniCreate(int mtu);
342c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native String jniGetName(int tun);
34397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh    private native int jniSetAddresses(String interfaze, String addresses);
34497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh    private native int jniSetRoutes(String interfaze, String routes);
345c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native void jniReset(String interfaze);
346c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native int jniCheck(String interfaze);
347c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native void jniProtect(int socket, String interfaze);
34885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
34985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    /**
3502e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     * Start legacy VPN. This method stops the daemons and restart them
3512e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     * if arguments are not null. Heavy things are offloaded to another
352e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * thread, so callers will not be blocked for a long time.
35385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     *
354e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @param config The parameters to configure the network.
35585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * @param raoocn The arguments to be passed to racoon.
35685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * @param mtpd The arguments to be passed to mtpd.
35785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     */
3582e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    public synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
359100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Prepare for the new request. This also checks the caller.
360100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        prepare(null, VpnConfig.LEGACY_VPN);
36185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
3622e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        // Start a new LegacyVpnRunner and we are done!
363100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
364100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        mLegacyVpnRunner.start();
36585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    }
36685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
36785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    /**
3682e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     * Return the information of the current ongoing legacy VPN.
3692e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     */
3702e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    public synchronized LegacyVpnInfo getLegacyVpnInfo() {
3712e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        // Only system user can call this method.
3722e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
3732e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            throw new SecurityException("Unauthorized Caller");
3742e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        }
3752e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        return (mLegacyVpnRunner == null) ? null : mLegacyVpnRunner.getInfo();
3762e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    }
3772e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
3782e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    /**
37985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * Bringing up a VPN connection takes time, and that is all this thread
38085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * does. Here we have plenty of time. The only thing we need to take
38185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * care of is responding to interruptions as soon as possible. Otherwise
38285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * requests will be piled up. This can be done in a Handler as a state
38385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * machine, but it is much easier to read in the current form.
38485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     */
38585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    private class LegacyVpnRunner extends Thread {
38685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private static final String TAG = "LegacyVpnRunner";
38785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
38841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        private final VpnConfig mConfig;
3891f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh        private final String[] mDaemons;
39085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private final String[][] mArguments;
3915317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh        private final LocalSocket[] mSockets;
392aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        private final String mOuterInterface;
3932e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        private final LegacyVpnInfo mInfo;
3942e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
39585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private long mTimer = -1;
39685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
39741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
39885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            super(TAG);
39941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mConfig = config;
40041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mDaemons = new String[] {"racoon", "mtpd"};
40141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mArguments = new String[][] {racoon, mtpd};
4025317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh            mSockets = new LocalSocket[mDaemons.length];
4032e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            mInfo = new LegacyVpnInfo();
404e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh
405aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh            // This is the interface which VPN is running on.
406aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh            mOuterInterface = mConfig.interfaze;
407aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh
4082e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            // Legacy VPN is not a real package, so we use it to carry the key.
409fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh            mInfo.key = mConfig.user;
410fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh            mConfig.user = VpnConfig.LEGACY_VPN;
41141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        }
41241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
413aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        public void check(String interfaze) {
414aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh            if (interfaze.equals(mOuterInterface)) {
415aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh                Log.i(TAG, "Legacy VPN is going down with " + interfaze);
416aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh                exit();
417aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh            }
418aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        }
419aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh
42041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        public void exit() {
4215317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh            // We assume that everything is reset after stopping the daemons.
42297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            interrupt();
4235317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh            for (LocalSocket socket : mSockets) {
4245317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                try {
4255317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    socket.close();
4265317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                } catch (Exception e) {
4275317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    // ignore
4285317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                }
42941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            }
43085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
43185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
4322e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        public LegacyVpnInfo getInfo() {
4332e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            // Update the info when VPN is disconnected.
4342e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            if (mInfo.state == LegacyVpnInfo.STATE_CONNECTED && mInterface == null) {
4352e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED;
4362e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                mInfo.intent = null;
4372e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            }
4382e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            return mInfo;
4392e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        }
4402e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
44185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        @Override
44285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        public void run() {
44385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            // Wait for the previous thread since it has been interrupted.
4442e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            Log.v(TAG, "Waiting");
44585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            synchronized (TAG) {
4462e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                Log.v(TAG, "Executing");
44785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                execute();
44885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
44985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
45085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
45185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private void checkpoint(boolean yield) throws InterruptedException {
45285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            long now = SystemClock.elapsedRealtime();
45385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            if (mTimer == -1) {
45485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                mTimer = now;
45585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Thread.sleep(1);
4567ef8611b5f3a893a46c7b9e22bdd8ab252e373ffChia-chi Yeh            } else if (now - mTimer <= 60000) {
45785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Thread.sleep(yield ? 200 : 1);
45885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } else {
4592e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                mInfo.state = LegacyVpnInfo.STATE_TIMEOUT;
46097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                throw new IllegalStateException("Time is up");
46185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
46285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
46385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
46485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private void execute() {
46585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            // Catch all exceptions so we can clean up few things.
46685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            try {
46785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // Initialize the timer.
46885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                checkpoint(false);
4692e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                mInfo.state = LegacyVpnInfo.STATE_INITIALIZING;
47085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
4711f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                // Wait for the daemons to stop.
4721f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                for (String daemon : mDaemons) {
4731f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    String key = "init.svc." + daemon;
4745317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    while (!"stopped".equals(SystemProperties.get(key, "stopped"))) {
47585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
47685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
47785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
47885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
47997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Clear the previous state.
48097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                File state = new File("/data/misc/vpn/state");
48197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                state.delete();
48297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (state.exists()) {
48397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    throw new IllegalStateException("Cannot delete the state");
48485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
485c1872732922214de80f790e14865e41dd1b98203Chia-chi Yeh                new File("/data/misc/vpn/abort").delete();
48685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
487e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                // Check if we need to restart any of the daemons.
48885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                boolean restart = false;
48985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                for (String[] arguments : mArguments) {
49085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    restart = restart || (arguments != null);
49185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
49285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                if (!restart) {
4932e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED;
49485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    return;
49585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
4962e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                mInfo.state = LegacyVpnInfo.STATE_CONNECTING;
49785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
4981f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                // Start the daemon with arguments.
4991f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                for (int i = 0; i < mDaemons.length; ++i) {
50085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    String[] arguments = mArguments[i];
50185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    if (arguments == null) {
50285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        continue;
50385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
50485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
5051f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Start the daemon.
5061f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    String daemon = mDaemons[i];
5071f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    SystemProperties.set("ctl.start", daemon);
50885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
5091f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Wait for the daemon to start.
5101f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    String key = "init.svc." + daemon;
51185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    while (!"running".equals(SystemProperties.get(key))) {
51285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
51385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
51485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
51585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Create the control socket.
5165317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    mSockets[i] = new LocalSocket();
51785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    LocalSocketAddress address = new LocalSocketAddress(
5181f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                            daemon, LocalSocketAddress.Namespace.RESERVED);
51985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
52085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Wait for the socket to connect.
52185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    while (true) {
52285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        try {
5235317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                            mSockets[i].connect(address);
52485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            break;
52585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        } catch (Exception e) {
52685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            // ignore
52785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
52885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
52985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
5305317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    mSockets[i].setSoTimeout(500);
53185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
53285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Send over the arguments.
5335317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    OutputStream out = mSockets[i].getOutputStream();
53485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    for (String argument : arguments) {
53585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        byte[] bytes = argument.getBytes(Charsets.UTF_8);
5365317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                        if (bytes.length >= 0xFFFF) {
53797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            throw new IllegalArgumentException("Argument is too large");
53885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
5391f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes.length >> 8);
5401f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes.length);
5411f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes);
54285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(false);
54385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
5445317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    out.write(0xFF);
5455317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    out.write(0xFF);
5461f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    out.flush();
54797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
54897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    // Wait for End-of-File.
5495317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    InputStream in = mSockets[i].getInputStream();
55097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    while (true) {
55197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        try {
55297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            if (in.read() == -1) {
55397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                                break;
55497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            }
55597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        } catch (Exception e) {
55697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            // ignore
55797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        }
55897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        checkpoint(true);
55997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    }
56085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
56185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
56297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Wait for the daemons to create the new state.
56397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                while (!state.exists()) {
5641f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Check if a running daemon is dead.
5651f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    for (int i = 0; i < mDaemons.length; ++i) {
5661f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        String daemon = mDaemons[i];
56785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        if (mArguments[i] != null && !"running".equals(
5681f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                                SystemProperties.get("init.svc." + daemon))) {
5692e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                            throw new IllegalStateException(daemon + " is dead");
57085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
57185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
57285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    checkpoint(true);
57385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
57485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
57597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Now we are connected. Read and parse the new state.
57697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                byte[] buffer = new byte[(int) state.length()];
57797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (new FileInputStream(state).read(buffer) != buffer.length) {
57897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    throw new IllegalStateException("Cannot read the state");
57997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
58097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                String[] parameters = new String(buffer, Charsets.UTF_8).split("\n", -1);
58197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (parameters.length != 6) {
58297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    throw new IllegalStateException("Cannot parse the state");
58397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
58497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
58597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the interface and the addresses in the config.
58697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                mConfig.interfaze = parameters[0].trim();
58797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                mConfig.addresses = parameters[1].trim();
58885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
58997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the routes if they are not set in the config.
59097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (mConfig.routes == null || mConfig.routes.isEmpty()) {
59197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    mConfig.routes = parameters[2].trim();
59297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
59397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
59497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the DNS servers if they are not set in the config.
59541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
59697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    String dnsServers = parameters[3].trim();
59741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    if (!dnsServers.isEmpty()) {
59841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                        mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
59941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    }
60041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                }
60141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
60297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the search domains if they are not set in the config.
60397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
60497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    String searchDomains = parameters[4].trim();
60597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    if (!searchDomains.isEmpty()) {
60697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
60797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    }
60897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
60997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
61097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the routes.
61197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                jniSetRoutes(mConfig.interfaze, mConfig.routes);
612e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh
61397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Here is the last step and it must be done synchronously.
61441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                synchronized (Vpn.this) {
61541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    // Check if the thread is interrupted while we are waiting.
61641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    checkpoint(false);
61741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
618e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                    // Check if the interface is gone while we are waiting.
619c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                    if (jniCheck(mConfig.interfaze) == 0) {
62034e7813e962de99df9813014678ef5901227c5f1Chia-chi Yeh                        throw new IllegalStateException(mConfig.interfaze + " is gone");
62141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    }
622e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh
623e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                    // Now INetworkManagementEventObserver is watching our back.
624c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                    mInterface = mConfig.interfaze;
62541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    mCallback.override(mConfig.dnsServers, mConfig.searchDomains);
62641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    showNotification(mConfig, null, null);
6272e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
6282e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    Log.i(TAG, "Connected!");
6292e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    mInfo.state = LegacyVpnInfo.STATE_CONNECTED;
6302e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    mInfo.intent = VpnConfig.getIntentForStatusPanel(mContext, null);
63141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                }
63285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } catch (Exception e) {
6332e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                Log.i(TAG, "Aborting", e);
634e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                exit();
6352e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            } finally {
6365317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                // Kill the daemons if they fail to stop.
6375317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING) {
6385317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    for (String daemon : mDaemons) {
6395317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                        SystemProperties.set("ctl.stop", daemon);
6405317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    }
6415317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                }
6425317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh
6432e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                // Do not leave an unstable state.
6442e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING ||
6452e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                        mInfo.state == LegacyVpnInfo.STATE_CONNECTING) {
6462e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    mInfo.state = LegacyVpnInfo.STATE_FAILED;
6472e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                }
64885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
64985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
65085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    }
651ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh}
652