Vpn.java revision 2e46764a707bd14cad22bc179669eeecb2d7c647
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;
21ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.Context;
22ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.Intent;
23ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.pm.ApplicationInfo;
24ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.pm.PackageManager;
25ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.res.Resources;
26ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.graphics.Bitmap;
27ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.graphics.Canvas;
28ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.graphics.drawable.Drawable;
29ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.net.INetworkManagementEventObserver;
3085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.net.LocalSocket;
3185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.net.LocalSocketAddress;
32ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.os.Binder;
33ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.os.ParcelFileDescriptor;
3485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.os.Process;
3585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.os.SystemClock;
3685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.os.SystemProperties;
37ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.util.Log;
38ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
39ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport com.android.internal.R;
402e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yehimport com.android.internal.net.LegacyVpnInfo;
4104ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yehimport com.android.internal.net.VpnConfig;
42ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport com.android.server.ConnectivityService.VpnCallback;
43ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
4485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport java.io.OutputStream;
4585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport java.nio.charset.Charsets;
4641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yehimport java.util.Arrays;
4785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
48ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh/**
49ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * @hide
50ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh */
51ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehpublic class Vpn extends INetworkManagementEventObserver.Stub {
52ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
53ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    private final static String TAG = "Vpn";
54ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    private final static String VPN = android.Manifest.permission.VPN;
55ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
56ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    private final Context mContext;
57ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    private final VpnCallback mCallback;
58ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
59c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private String mPackage = VpnConfig.LEGACY_VPN;
60c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private String mInterface;
6185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    private LegacyVpnRunner mLegacyVpnRunner;
62ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
63ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    public Vpn(Context context, VpnCallback callback) {
64ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        mContext = context;
65ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        mCallback = callback;
66ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
67ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
68ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    /**
69e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * Protect a socket from routing changes by binding it to the given
70e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * interface. The socket IS closed by this method.
71ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     *
72e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @param socket The socket to be bound.
73e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @param name The name of the interface.
74e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     */
75c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    public void protect(ParcelFileDescriptor socket, String interfaze) {
76e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh        try {
77e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh            mContext.enforceCallingPermission(VPN, "protect");
78c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            jniProtect(socket.getFd(), interfaze);
79e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh        } finally {
80e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh            try {
81e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                socket.close();
82e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh            } catch (Exception e) {
83e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                // ignore
84e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh            }
85e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh        }
86e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh    }
87e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh
88e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh    /**
89100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * Prepare for a VPN application. This method is designed to solve
90100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * race conditions. It first compares the current prepared package
91100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * with {@code oldPackage}. If they are the same, the prepared
92100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * package is revoked and replaced with {@code newPackage}. If
93100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * {@code oldPackage} is {@code null}, the comparison is omitted.
94100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * If {@code newPackage} is the same package or {@code null}, the
95100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * revocation is omitted. This method returns {@code true} if the
96100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * operation is succeeded.
97e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     *
98100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * Legacy VPN is handled specially since it is not a real package.
99100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
100100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * it can be revoked by itself.
101100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     *
102100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @param oldPackage The package name of the old VPN application.
103100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @param newPackage The package name of the new VPN application.
104100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @return true if the operation is succeeded.
105ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
106100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh    public synchronized boolean prepare(String oldPackage, String newPackage) {
107100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Return false if the package does not match.
108c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        if (oldPackage != null && !oldPackage.equals(mPackage)) {
109100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh            return false;
110100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        }
111100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh
112100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Return true if we do not need to revoke.
113100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        if (newPackage == null ||
114c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                (newPackage.equals(mPackage) && !newPackage.equals(VpnConfig.LEGACY_VPN))) {
115100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh            return true;
116ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
117ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
118100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Only system user can revoke a package.
11941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
12041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            throw new SecurityException("Unauthorized Caller");
12141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        }
1227b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh
1237b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh        // Check the permission of the given package.
12441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        PackageManager pm = mContext.getPackageManager();
125100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        if (!newPackage.equals(VpnConfig.LEGACY_VPN) &&
126100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh                pm.checkPermission(VPN, newPackage) != PackageManager.PERMISSION_GRANTED) {
127100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh            throw new SecurityException(newPackage + " does not have " + VPN);
128ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
129ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
130ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        // Reset the interface and hide the notification.
131c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        if (mInterface != null) {
132c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            jniReset(mInterface);
1337b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            mCallback.restore();
134ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            hideNotification();
135c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            mInterface = null;
136ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
137ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
138e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh        // Send out the broadcast or stop LegacyVpnRunner.
139c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        if (!mPackage.equals(VpnConfig.LEGACY_VPN)) {
1407b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            Intent intent = new Intent(VpnConfig.ACTION_VPN_REVOKED);
141c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            intent.setPackage(mPackage);
1427b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
1437b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            mContext.sendBroadcast(intent);
144e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh        } else if (mLegacyVpnRunner != null) {
14541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mLegacyVpnRunner.exit();
14641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mLegacyVpnRunner = null;
14741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        }
14841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
149c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
150c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        mPackage = newPackage;
151100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        return true;
152ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
153ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
154ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    /**
155e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * Establish a VPN network and return the file descriptor of the VPN
156e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * interface. This methods returns {@code null} if the application is
157100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * revoked or not prepared.
158ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     *
159e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @param config The parameters to configure the network.
160e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @return The file descriptor of the VPN interface.
161ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
16204ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yeh    public synchronized ParcelFileDescriptor establish(VpnConfig config) {
163ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        // Check the permission of the caller.
164ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        mContext.enforceCallingPermission(VPN, "establish");
165ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
166ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        // Check if the caller is already prepared.
167ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        PackageManager pm = mContext.getPackageManager();
168ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        ApplicationInfo app = null;
169ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        try {
170c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            app = pm.getApplicationInfo(mPackage, 0);
171ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        } catch (Exception e) {
1727b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            return null;
173ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
174ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        if (Binder.getCallingUid() != app.uid) {
1757b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            return null;
176ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
177ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
178a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        // Load the label.
179a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        String label = app.loadLabel(pm).toString();
180a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh
181a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        // Load the icon and convert it into a bitmap.
182a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        Drawable icon = app.loadIcon(pm);
183a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        Bitmap bitmap = null;
184a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
185a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            int width = mContext.getResources().getDimensionPixelSize(
186a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh                    android.R.dimen.notification_large_icon_width);
187a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            int height = mContext.getResources().getDimensionPixelSize(
188a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh                    android.R.dimen.notification_large_icon_height);
189a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            icon.setBounds(0, 0, width, height);
190a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
191a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            icon.draw(new Canvas(bitmap));
192a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        }
193a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh
194e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh        // Configure the interface. Abort if any of these steps fails.
195c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(
1963281034c1c458b4eecd867d20b64dc5edd68ec14Chia-chi Yeh                jniConfigure(config.mtu, config.addresses, config.routes));
197ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        try {
198c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            String interfaze = jniGetName(tun.getFd());
199c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            if (mInterface != null && !mInterface.equals(interfaze)) {
200c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                jniReset(mInterface);
201ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            }
202c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            mInterface = interfaze;
203ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        } catch (RuntimeException e) {
204ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            try {
205c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                tun.close();
206ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            } catch (Exception ex) {
207ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                // ignore
208ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            }
209ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            throw e;
210ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
211ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
2128909b10175303bd5e2ca82b7ba12cd0017050ef3Chia-chi Yeh        // Override DNS servers and search domains.
2138909b10175303bd5e2ca82b7ba12cd0017050ef3Chia-chi Yeh        mCallback.override(config.dnsServers, config.searchDomains);
214ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
2158909b10175303bd5e2ca82b7ba12cd0017050ef3Chia-chi Yeh        // Fill more values.
216c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        config.packagz = mPackage;
217c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        config.interfaze = mInterface;
2188909b10175303bd5e2ca82b7ba12cd0017050ef3Chia-chi Yeh
2198909b10175303bd5e2ca82b7ba12cd0017050ef3Chia-chi Yeh        // Show the notification!
220383e0524726d64302322abeba16d87faf66bae99Chia-chi Yeh        showNotification(config, label, bitmap);
221c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        return tun;
222ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
223ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
224ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    // INetworkManagementEventObserver.Stub
225c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    public void interfaceStatusChanged(String interfaze, boolean up) {
226f59c7d0f2ac8d489b6d8118543a57ea4a603eacfMike J. Chen    }
227f59c7d0f2ac8d489b6d8118543a57ea4a603eacfMike J. Chen
228f59c7d0f2ac8d489b6d8118543a57ea4a603eacfMike J. Chen    // INetworkManagementEventObserver.Stub
229c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    public void interfaceLinkStateChanged(String interfaze, boolean up) {
230ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
231ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
232ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    // INetworkManagementEventObserver.Stub
233c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    public void interfaceAdded(String interfaze) {
234ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
235ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
236ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    // INetworkManagementEventObserver.Stub
237c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    public synchronized void interfaceRemoved(String interfaze) {
238c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
239ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            mCallback.restore();
240e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh            hideNotification();
241c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            mInterface = null;
242ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
243ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
244ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
245a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh    private void showNotification(VpnConfig config, String label, Bitmap icon) {
246ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        NotificationManager nm = (NotificationManager)
247ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
248ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
249ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        if (nm != null) {
250a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            String title = (label == null) ? mContext.getString(R.string.vpn_title) :
251a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh                    mContext.getString(R.string.vpn_title_long, label);
25234e7813e962de99df9813014678ef5901227c5f1Chia-chi Yeh            String text = (config.session == null) ? mContext.getString(R.string.vpn_text) :
25334e7813e962de99df9813014678ef5901227c5f1Chia-chi Yeh                    mContext.getString(R.string.vpn_text_long, config.session);
2542e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            config.startTime = SystemClock.elapsedRealtime();
255a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh
256ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            long identity = Binder.clearCallingIdentity();
257ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            Notification notification = new Notification.Builder(mContext)
258ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                    .setSmallIcon(R.drawable.vpn_connected)
259a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh                    .setLargeIcon(icon)
260a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh                    .setContentTitle(title)
261f8905fd13da0bfd6049daebc1cf4f8af286a04deChia-chi Yeh                    .setContentText(text)
2622e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    .setContentIntent(VpnConfig.getIntentForStatusPanel(mContext, config))
263ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                    .setDefaults(Notification.DEFAULT_ALL)
264ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                    .setOngoing(true)
265ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                    .getNotification();
266ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            nm.notify(R.drawable.vpn_connected, notification);
267ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            Binder.restoreCallingIdentity(identity);
268ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
269ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
270ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
271ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    private void hideNotification() {
272ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        NotificationManager nm = (NotificationManager)
273ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
274ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
275ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        if (nm != null) {
276ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            long identity = Binder.clearCallingIdentity();
277ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            nm.cancel(R.drawable.vpn_connected);
278ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            Binder.restoreCallingIdentity(identity);
279ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
280ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
281ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
2823281034c1c458b4eecd867d20b64dc5edd68ec14Chia-chi Yeh    private native int jniConfigure(int mtu, String addresses, String routes);
283c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native String jniGetName(int tun);
284c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native void jniReset(String interfaze);
285c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native int jniCheck(String interfaze);
286c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native void jniProtect(int socket, String interfaze);
28785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
28885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    /**
2892e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     * Start legacy VPN. This method stops the daemons and restart them
2902e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     * if arguments are not null. Heavy things are offloaded to another
291e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * thread, so callers will not be blocked for a long time.
29285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     *
293e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @param config The parameters to configure the network.
29485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * @param raoocn The arguments to be passed to racoon.
29585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * @param mtpd The arguments to be passed to mtpd.
29685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     */
2972e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    public synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
298100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Prepare for the new request. This also checks the caller.
299100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        prepare(null, VpnConfig.LEGACY_VPN);
30085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
3012e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        // Start a new LegacyVpnRunner and we are done!
302100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
303100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        mLegacyVpnRunner.start();
30485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    }
30585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
30685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    /**
3072e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     * Return the information of the current ongoing legacy VPN.
3082e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     */
3092e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    public synchronized LegacyVpnInfo getLegacyVpnInfo() {
3102e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        // Only system user can call this method.
3112e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
3122e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            throw new SecurityException("Unauthorized Caller");
3132e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        }
3142e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        return (mLegacyVpnRunner == null) ? null : mLegacyVpnRunner.getInfo();
3152e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    }
3162e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
3172e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    /**
31885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * Bringing up a VPN connection takes time, and that is all this thread
31985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * does. Here we have plenty of time. The only thing we need to take
32085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * care of is responding to interruptions as soon as possible. Otherwise
32185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * requests will be piled up. This can be done in a Handler as a state
32285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * machine, but it is much easier to read in the current form.
32385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     */
32485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    private class LegacyVpnRunner extends Thread {
32585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private static final String TAG = "LegacyVpnRunner";
32685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private static final String NONE = "--";
32785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
32841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        private final VpnConfig mConfig;
3291f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh        private final String[] mDaemons;
33085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private final String[][] mArguments;
3312e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        private final LegacyVpnInfo mInfo;
3322e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
33385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private long mTimer = -1;
33485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
33541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
33685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            super(TAG);
33741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mConfig = config;
33841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mDaemons = new String[] {"racoon", "mtpd"};
33941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mArguments = new String[][] {racoon, mtpd};
3402e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            mInfo = new LegacyVpnInfo();
341e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh
3422e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            // Legacy VPN is not a real package, so we use it to carry the key.
3432e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            mInfo.key = mConfig.packagz;
34434e7813e962de99df9813014678ef5901227c5f1Chia-chi Yeh            mConfig.packagz = VpnConfig.LEGACY_VPN;
34541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        }
34641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
34741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        public void exit() {
348e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh            // We assume that everything is reset after the daemons die.
34941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            for (String daemon : mDaemons) {
35041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                SystemProperties.set("ctl.stop", daemon);
35141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            }
35241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            interrupt();
35385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
35485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
3552e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        public LegacyVpnInfo getInfo() {
3562e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            // Update the info when VPN is disconnected.
3572e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            if (mInfo.state == LegacyVpnInfo.STATE_CONNECTED && mInterface == null) {
3582e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED;
3592e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                mInfo.intent = null;
3602e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            }
3612e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            return mInfo;
3622e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        }
3632e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
36485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        @Override
36585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        public void run() {
36685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            // Wait for the previous thread since it has been interrupted.
3672e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            Log.v(TAG, "Waiting");
36885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            synchronized (TAG) {
3692e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                Log.v(TAG, "Executing");
37085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                execute();
37185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
37285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
37385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
37485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private void checkpoint(boolean yield) throws InterruptedException {
37585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            long now = SystemClock.elapsedRealtime();
37685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            if (mTimer == -1) {
37785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                mTimer = now;
37885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Thread.sleep(1);
37985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } else if (now - mTimer <= 30000) {
38085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Thread.sleep(yield ? 200 : 1);
38185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } else {
3822e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                mInfo.state = LegacyVpnInfo.STATE_TIMEOUT;
3832e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                throw new IllegalStateException("time is up");
38485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
38585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
38685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
38785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private void execute() {
38885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            // Catch all exceptions so we can clean up few things.
38985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            try {
39085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // Initialize the timer.
39185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                checkpoint(false);
3922e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                mInfo.state = LegacyVpnInfo.STATE_INITIALIZING;
39385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
3941f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                // First stop the daemons.
3951f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                for (String daemon : mDaemons) {
3961f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    SystemProperties.set("ctl.stop", daemon);
39785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
39885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
3991f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                // Wait for the daemons to stop.
4001f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                for (String daemon : mDaemons) {
4011f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    String key = "init.svc." + daemon;
40285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    while (!"stopped".equals(SystemProperties.get(key))) {
40385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
40485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
40585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
40685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
40785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // Reset the properties.
40885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                SystemProperties.set("vpn.dns", NONE);
40985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                SystemProperties.set("vpn.via", NONE);
41085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                while (!NONE.equals(SystemProperties.get("vpn.dns")) ||
41185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        !NONE.equals(SystemProperties.get("vpn.via"))) {
41285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    checkpoint(true);
41385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
41485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
415e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                // Check if we need to restart any of the daemons.
41685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                boolean restart = false;
41785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                for (String[] arguments : mArguments) {
41885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    restart = restart || (arguments != null);
41985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
42085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                if (!restart) {
4212e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED;
42285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    return;
42385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
4242e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                mInfo.state = LegacyVpnInfo.STATE_CONNECTING;
42585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
4261f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                // Start the daemon with arguments.
4271f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                for (int i = 0; i < mDaemons.length; ++i) {
42885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    String[] arguments = mArguments[i];
42985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    if (arguments == null) {
43085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        continue;
43185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
43285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
4331f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Start the daemon.
4341f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    String daemon = mDaemons[i];
4351f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    SystemProperties.set("ctl.start", daemon);
43685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
4371f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Wait for the daemon to start.
4381f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    String key = "init.svc." + daemon;
43985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    while (!"running".equals(SystemProperties.get(key))) {
44085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
44185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
44285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
44385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Create the control socket.
44485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    LocalSocket socket = new LocalSocket();
44585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    LocalSocketAddress address = new LocalSocketAddress(
4461f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                            daemon, LocalSocketAddress.Namespace.RESERVED);
44785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
44885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Wait for the socket to connect.
44985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    while (true) {
45085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        try {
45185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            socket.connect(address);
45285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            break;
45385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        } catch (Exception e) {
45485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            // ignore
45585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
45685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
45785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
45885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    socket.setSoTimeout(500);
45985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
46085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Send over the arguments.
4611f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    OutputStream out = socket.getOutputStream();
46285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    for (String argument : arguments) {
46385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        byte[] bytes = argument.getBytes(Charsets.UTF_8);
46485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        if (bytes.length >= 0xFFFF) {
4653281034c1c458b4eecd867d20b64dc5edd68ec14Chia-chi Yeh                            throw new IllegalArgumentException("argument is too large");
46685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
4671f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes.length >> 8);
4681f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes.length);
4691f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes);
47085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(false);
47185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
47285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
47385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Send End-Of-Arguments.
4741f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    out.write(0xFF);
4751f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    out.write(0xFF);
4761f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    out.flush();
47785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    socket.close();
47885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
47985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
48085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // Now here is the beast from the old days. We check few
48185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // properties to figure out the current status. Ideally we
48285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // can read things back from the sockets and get rid of the
48385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // properties, but we have no time...
48485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                while (NONE.equals(SystemProperties.get("vpn.dns")) ||
48585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        NONE.equals(SystemProperties.get("vpn.via"))) {
48685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
4871f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Check if a running daemon is dead.
4881f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    for (int i = 0; i < mDaemons.length; ++i) {
4891f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        String daemon = mDaemons[i];
49085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        if (mArguments[i] != null && !"running".equals(
4911f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                                SystemProperties.get("init.svc." + daemon))) {
4922e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                            throw new IllegalStateException(daemon + " is dead");
49385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
49485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
49585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    checkpoint(true);
49685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
49785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
49841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                // Now we are connected. Get the interface.
49934e7813e962de99df9813014678ef5901227c5f1Chia-chi Yeh                mConfig.interfaze = SystemProperties.get("vpn.via");
50085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
50141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                // Get the DNS servers if they are not set in config.
50241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
50341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    String dnsServers = SystemProperties.get("vpn.dns").trim();
50441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    if (!dnsServers.isEmpty()) {
50541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                        mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
50641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    }
50741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                }
50841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
5093281034c1c458b4eecd867d20b64dc5edd68ec14Chia-chi Yeh                // TODO: support search domains from ISAKMP mode config.
510e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh
511e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                // The final step must be synchronized.
51241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                synchronized (Vpn.this) {
51341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    // Check if the thread is interrupted while we are waiting.
51441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    checkpoint(false);
51541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
516e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                    // Check if the interface is gone while we are waiting.
517c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                    if (jniCheck(mConfig.interfaze) == 0) {
51834e7813e962de99df9813014678ef5901227c5f1Chia-chi Yeh                        throw new IllegalStateException(mConfig.interfaze + " is gone");
51941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    }
520e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh
521e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                    // Now INetworkManagementEventObserver is watching our back.
522c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                    mInterface = mConfig.interfaze;
52341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    mCallback.override(mConfig.dnsServers, mConfig.searchDomains);
52441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    showNotification(mConfig, null, null);
5252e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
5262e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    Log.i(TAG, "Connected!");
5272e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    mInfo.state = LegacyVpnInfo.STATE_CONNECTED;
5282e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    mInfo.intent = VpnConfig.getIntentForStatusPanel(mContext, null);
52941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                }
53085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } catch (Exception e) {
5312e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                Log.i(TAG, "Aborting", e);
532e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                exit();
5332e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            } finally {
5342e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                // Do not leave an unstable state.
5352e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING ||
5362e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                        mInfo.state == LegacyVpnInfo.STATE_CONNECTING) {
5372e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    mInfo.state = LegacyVpnInfo.STATE_FAILED;
5382e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                }
53985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
54085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
54185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    }
542ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh}
543