Vpn.java revision a4b87b5e980ffa52e9bc5549688b588b1b99a1eb
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;
4004ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yehimport com.android.internal.net.VpnConfig;
41ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport com.android.server.ConnectivityService.VpnCallback;
42ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
4385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport java.io.OutputStream;
4485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport java.nio.charset.Charsets;
4585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
46ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh/**
47ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * @hide
48ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh */
49ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehpublic class Vpn extends INetworkManagementEventObserver.Stub {
50ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
51ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    private final static String TAG = "Vpn";
52ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    private final static String VPN = android.Manifest.permission.VPN;
53ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
54ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    private final Context mContext;
55ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    private final VpnCallback mCallback;
56ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
57ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    private String mPackageName;
58ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    private String mInterfaceName;
5985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
6085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    private LegacyVpnRunner mLegacyVpnRunner;
61ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
62ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    public Vpn(Context context, VpnCallback callback) {
63ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        mContext = context;
64ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        mCallback = callback;
65ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
66ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
67ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    /**
68ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     * Prepare for a VPN application.
69ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     *
70ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     * @param packageName The package name of the new VPN application.
71ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     * @return The name of the current prepared package.
72ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
73ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    public synchronized String prepare(String packageName) {
747b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh        // Return the current prepared package if the new one is null.
75ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        if (packageName == null) {
76ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            return mPackageName;
77ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
78ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
797b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh        // Check the permission of the caller.
80ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        PackageManager pm = mContext.getPackageManager();
817b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh        VpnConfig.enforceCallingPackage(pm.getNameForUid(Binder.getCallingUid()));
827b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh
837b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh        // Check the permission of the given package.
847b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh        if (packageName.isEmpty()) {
857b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            packageName = null;
867b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh        } else if (pm.checkPermission(VPN, packageName) != PackageManager.PERMISSION_GRANTED) {
87ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            throw new SecurityException(packageName + " does not have " + VPN);
88ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
89ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
90ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        // Reset the interface and hide the notification.
91ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        if (mInterfaceName != null) {
92f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh            jniResetInterface(mInterfaceName);
937b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            mCallback.restore();
94ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            hideNotification();
957b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            mInterfaceName = null;
96ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
97ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
987b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh        // Notify the package being revoked.
997b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh        if (mPackageName != null) {
1007b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            Intent intent = new Intent(VpnConfig.ACTION_VPN_REVOKED);
1017b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            intent.setPackage(mPackageName);
1027b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
1037b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            mContext.sendBroadcast(intent);
1047b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh        }
1057b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh
1067b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh        Log.i(TAG, "Switched from " + mPackageName + " to " + packageName);
107ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        mPackageName = packageName;
108ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        return mPackageName;
109ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
110ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
111ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    /**
112ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     * Protect a socket from routing changes by binding it to the given
1133f3337a662e9916bbf14502ef3b32dedaa7adfa4Chia-chi Yeh     * interface. The socket IS closed by this method.
114ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     *
115ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     * @param socket The socket to be bound.
116ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     * @param name The name of the interface.
117ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
118ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    public void protect(ParcelFileDescriptor socket, String name) {
1193f3337a662e9916bbf14502ef3b32dedaa7adfa4Chia-chi Yeh        try {
1203f3337a662e9916bbf14502ef3b32dedaa7adfa4Chia-chi Yeh            mContext.enforceCallingPermission(VPN, "protect");
121f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh            jniProtectSocket(socket.getFd(), name);
1223f3337a662e9916bbf14502ef3b32dedaa7adfa4Chia-chi Yeh        } finally {
1233f3337a662e9916bbf14502ef3b32dedaa7adfa4Chia-chi Yeh            try {
1243f3337a662e9916bbf14502ef3b32dedaa7adfa4Chia-chi Yeh                socket.close();
1253f3337a662e9916bbf14502ef3b32dedaa7adfa4Chia-chi Yeh            } catch (Exception e) {
1263f3337a662e9916bbf14502ef3b32dedaa7adfa4Chia-chi Yeh                // ignore
1273f3337a662e9916bbf14502ef3b32dedaa7adfa4Chia-chi Yeh            }
1283f3337a662e9916bbf14502ef3b32dedaa7adfa4Chia-chi Yeh        }
129ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
130ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
131ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    /**
132ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     * Configure a TUN interface and return its file descriptor.
133ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     *
134a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh     * @param config The parameters to configure the interface.
135ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     * @return The file descriptor of the interface.
136ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
13704ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yeh    public synchronized ParcelFileDescriptor establish(VpnConfig config) {
138ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        // Check the permission of the caller.
139ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        mContext.enforceCallingPermission(VPN, "establish");
140ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
141ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        // Check if the caller is already prepared.
142ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        PackageManager pm = mContext.getPackageManager();
143ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        ApplicationInfo app = null;
144ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        try {
145ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            app = pm.getApplicationInfo(mPackageName, 0);
146ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        } catch (Exception e) {
1477b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            return null;
148ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
149ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        if (Binder.getCallingUid() != app.uid) {
1507b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            return null;
151ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
152ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
153a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        // Load the label.
154a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        String label = app.loadLabel(pm).toString();
155a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh
156a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        // Load the icon and convert it into a bitmap.
157a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        Drawable icon = app.loadIcon(pm);
158a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        Bitmap bitmap = null;
159a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
160a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            int width = mContext.getResources().getDimensionPixelSize(
161a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh                    android.R.dimen.notification_large_icon_width);
162a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            int height = mContext.getResources().getDimensionPixelSize(
163a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh                    android.R.dimen.notification_large_icon_height);
164a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            icon.setBounds(0, 0, width, height);
165a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
166a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            icon.draw(new Canvas(bitmap));
167a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        }
168a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh
169a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh        // Create the interface and abort if any of the following steps fails.
170f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh        ParcelFileDescriptor descriptor =
171f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh                ParcelFileDescriptor.adoptFd(jniCreateInterface(config.mtu));
172ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        try {
173f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh            String name = jniGetInterfaceName(descriptor.getFd());
174f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh            if (jniSetAddresses(name, config.addresses) < 1) {
175f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh                throw new IllegalArgumentException("At least one address must be specified");
176f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh            }
177f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh            if (config.routes != null) {
178f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh                jniSetRoutes(name, config.routes);
179f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh            }
180f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh            if (mInterfaceName != null && !mInterfaceName.equals(name)) {
181f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh                jniResetInterface(mInterfaceName);
182ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            }
183f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh            mInterfaceName = name;
184ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        } catch (RuntimeException e) {
185ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            try {
186ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                descriptor.close();
187ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            } catch (Exception ex) {
188ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                // ignore
189ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            }
190ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            throw e;
191ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
192ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
19304ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yeh        String dnsServers = (config.dnsServers == null) ? "" : config.dnsServers.trim();
194ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        mCallback.override(dnsServers.isEmpty() ? null : dnsServers.split(" "));
195ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
1967b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh        config.packageName = mPackageName;
1977b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh        config.interfaceName = mInterfaceName;
1987b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh        showNotification(pm, app, config);
199ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        return descriptor;
200ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
201ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
202ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    // INetworkManagementEventObserver.Stub
203f59c7d0f2ac8d489b6d8118543a57ea4a603eacfMike J. Chen    public void interfaceStatusChanged(String name, boolean up) {
204f59c7d0f2ac8d489b6d8118543a57ea4a603eacfMike J. Chen    }
205f59c7d0f2ac8d489b6d8118543a57ea4a603eacfMike J. Chen
206f59c7d0f2ac8d489b6d8118543a57ea4a603eacfMike J. Chen    // INetworkManagementEventObserver.Stub
207f59c7d0f2ac8d489b6d8118543a57ea4a603eacfMike J. Chen    public void interfaceLinkStateChanged(String name, boolean up) {
208ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
209ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
210ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    // INetworkManagementEventObserver.Stub
211ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    public void interfaceAdded(String name) {
212ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
213ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
214ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    // INetworkManagementEventObserver.Stub
215ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    public synchronized void interfaceRemoved(String name) {
216f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh        if (name.equals(mInterfaceName) && jniCheckInterface(name) == 0) {
217ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            hideNotification();
218ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            mCallback.restore();
219a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            mInterfaceName = null;
220ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
221ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
222ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
223a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh    private void showNotification(VpnConfig config, String label, Bitmap icon) {
224ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        NotificationManager nm = (NotificationManager)
225ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
226ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
227ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        if (nm != null) {
228a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh            String title = (label == null) ? mContext.getString(R.string.vpn_title) :
229a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh                    mContext.getString(R.string.vpn_title_long, label);
2307b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            String text = (config.sessionName == null) ? mContext.getString(R.string.vpn_text) :
2317b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh                    mContext.getString(R.string.vpn_text_long, config.sessionName);
232a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh
233ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            long identity = Binder.clearCallingIdentity();
234ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            Notification notification = new Notification.Builder(mContext)
235ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                    .setSmallIcon(R.drawable.vpn_connected)
236a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh                    .setLargeIcon(icon)
237a4b87b5e980ffa52e9bc5549688b588b1b99a1ebChia-chi Yeh                    .setContentTitle(title)
238f8905fd13da0bfd6049daebc1cf4f8af286a04deChia-chi Yeh                    .setContentText(text)
2397b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh                    .setContentIntent(VpnConfig.getIntentForNotification(mContext, config))
240ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                    .setDefaults(Notification.DEFAULT_ALL)
241ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                    .setOngoing(true)
242ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                    .getNotification();
243ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            nm.notify(R.drawable.vpn_connected, notification);
244ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            Binder.restoreCallingIdentity(identity);
245ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
246ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
247ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
248ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    private void hideNotification() {
249ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        NotificationManager nm = (NotificationManager)
250ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
251ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
252ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        if (nm != null) {
253ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            long identity = Binder.clearCallingIdentity();
254ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            nm.cancel(R.drawable.vpn_connected);
255ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            Binder.restoreCallingIdentity(identity);
256ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
257ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
258ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
259f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh    private native int jniCreateInterface(int mtu);
260f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh    private native String jniGetInterfaceName(int fd);
261f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh    private native int jniSetAddresses(String name, String addresses);
262f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh    private native int jniSetRoutes(String name, String routes);
263f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh    private native void jniResetInterface(String name);
264f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh    private native int jniCheckInterface(String name);
265f4e3bf892e593d8c74290739446ac205fe8c66b2Chia-chi Yeh    private native void jniProtectSocket(int fd, String name);
26685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
26785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    /**
26885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * Handle legacy VPN requests. This method stops the services and restart
26985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * them if their arguments are not null. Heavy things are offloaded to
27085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * another thread, so callers will not be blocked too long.
27185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     *
27285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * @param raoocn The arguments to be passed to racoon.
27385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * @param mtpd The arguments to be passed to mtpd.
27485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     */
27585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    public synchronized void doLegacyVpn(String[] racoon, String[] mtpd) {
27685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        // Currently only system user is allowed.
27785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
27885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            throw new SecurityException("Unauthorized Caller");
27985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
28085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
28185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        // If the previous runner is still alive, interrupt it.
28285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        if (mLegacyVpnRunner != null && mLegacyVpnRunner.isAlive()) {
28385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            mLegacyVpnRunner.interrupt();
28485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
28585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
28685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        // Start a new runner and we are done!
28785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        mLegacyVpnRunner = new LegacyVpnRunner(
28885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                new String[] {"racoon", "mtpd"}, racoon, mtpd);
28985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        mLegacyVpnRunner.start();
29085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    }
29185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
29285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    /**
29385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * Bringing up a VPN connection takes time, and that is all this thread
29485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * does. Here we have plenty of time. The only thing we need to take
29585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * care of is responding to interruptions as soon as possible. Otherwise
29685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * requests will be piled up. This can be done in a Handler as a state
29785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * machine, but it is much easier to read in the current form.
29885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     */
29985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    private class LegacyVpnRunner extends Thread {
30085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private static final String TAG = "LegacyVpnRunner";
30185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
30285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private static final String NONE = "--";
30385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
30485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private final String[] mServices;
30585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private final String[][] mArguments;
30685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private long mTimer = -1;
30785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
30885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        public LegacyVpnRunner(String[] services, String[]... arguments) {
30985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            super(TAG);
31085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            mServices = services;
31185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            mArguments = arguments;
31285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
31385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
31485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        @Override
31585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        public void run() {
31685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            // Wait for the previous thread since it has been interrupted.
31785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            Log.v(TAG, "wait");
31885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            synchronized (TAG) {
31985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Log.v(TAG, "run");
32085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                execute();
32185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Log.v(TAG, "exit");
32285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
32385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
32485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
32585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private void checkpoint(boolean yield) throws InterruptedException {
32685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            long now = SystemClock.elapsedRealtime();
32785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            if (mTimer == -1) {
32885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                mTimer = now;
32985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Thread.sleep(1);
33085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } else if (now - mTimer <= 30000) {
33185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Thread.sleep(yield ? 200 : 1);
33285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } else {
33385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                throw new InterruptedException("timeout");
33485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
33585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
33685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
33785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private void execute() {
33885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            // Catch all exceptions so we can clean up few things.
33985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            try {
34085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // Initialize the timer.
34185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                checkpoint(false);
34285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
34385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // First stop the services.
34485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                for (String service : mServices) {
34585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    SystemProperties.set("ctl.stop", service);
34685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
34785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
34885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // Wait for the services to stop.
34985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                for (String service : mServices) {
35085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    String key = "init.svc." + service;
35185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    while (!"stopped".equals(SystemProperties.get(key))) {
35285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
35385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
35485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
35585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
35685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // Reset the properties.
35785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                SystemProperties.set("vpn.dns", NONE);
35885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                SystemProperties.set("vpn.via", NONE);
35985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                while (!NONE.equals(SystemProperties.get("vpn.dns")) ||
36085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        !NONE.equals(SystemProperties.get("vpn.via"))) {
36185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    checkpoint(true);
36285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
36385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
36485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // Check if we need to restart some services.
36585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                boolean restart = false;
36685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                for (String[] arguments : mArguments) {
36785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    restart = restart || (arguments != null);
36885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
36985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                if (!restart) {
37085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    return;
37185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
37285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
37385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // Start the service with arguments.
37485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                for (int i = 0; i < mServices.length; ++i) {
37585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    String[] arguments = mArguments[i];
37685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    if (arguments == null) {
37785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        continue;
37885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
37985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
38085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Start the service.
38185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    String service = mServices[i];
38285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    SystemProperties.set("ctl.start", service);
38385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
38485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Wait for the service to start.
38585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    String key = "init.svc." + service;
38685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    while (!"running".equals(SystemProperties.get(key))) {
38785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
38885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
38985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
39085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Create the control socket.
39185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    LocalSocket socket = new LocalSocket();
39285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    LocalSocketAddress address = new LocalSocketAddress(
39385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            service, LocalSocketAddress.Namespace.RESERVED);
39485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
39585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Wait for the socket to connect.
39685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    while (true) {
39785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        try {
39885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            socket.connect(address);
39985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            break;
40085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        } catch (Exception e) {
40185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            // ignore
40285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
40385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
40485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
40585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    socket.setSoTimeout(500);
40685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
40785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Send over the arguments.
40885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    OutputStream output = socket.getOutputStream();
40985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    for (String argument : arguments) {
41085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        byte[] bytes = argument.getBytes(Charsets.UTF_8);
41185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        if (bytes.length >= 0xFFFF) {
41285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            throw new IllegalArgumentException("argument too large");
41385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
41485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        output.write(bytes.length >> 8);
41585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        output.write(bytes.length);
41685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        output.write(bytes);
41785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(false);
41885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
41985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
42085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Send End-Of-Arguments.
42185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    output.write(0xFF);
42285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    output.write(0xFF);
42385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    output.flush();
42485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    socket.close();
42585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
42685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
42785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // Now here is the beast from the old days. We check few
42885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // properties to figure out the current status. Ideally we
42985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // can read things back from the sockets and get rid of the
43085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // properties, but we have no time...
43185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                while (NONE.equals(SystemProperties.get("vpn.dns")) ||
43285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        NONE.equals(SystemProperties.get("vpn.via"))) {
43385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
43485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Check if a running service is dead.
43585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    for (int i = 0; i < mServices.length; ++i) {
43685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        String service = mServices[i];
43785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        if (mArguments[i] != null && !"running".equals(
43885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                                SystemProperties.get("init.svc." + service))) {
43985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            throw new IllegalArgumentException(service + " is dead");
44085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
44185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
44285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    checkpoint(true);
44385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
44485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
44585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // Great! Now we are connected!
44685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Log.i(TAG, "connected!");
44785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // TODO:
44885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
44985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } catch (Exception e) {
45085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Log.i(TAG, e.getMessage());
45185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                for (String service : mServices) {
45285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    SystemProperties.set("ctl.stop", service);
45385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
45485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
45585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
45685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    }
457ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh}
458