├── AndroidManifest.xml ├── VpnServices ├── Android.mk ├── AndroidManifest.xml ├── MODULE_LICENSE_APACHE2 ├── NOTICE └── src │ └── com │ └── android │ └── server │ └── vpn │ ├── DaemonProxy.java │ ├── L2tpIpsecPskService.java │ ├── L2tpIpsecService.java │ ├── L2tpService.java │ ├── PptpService.java │ ├── VpnConnectingError.java │ ├── VpnDaemons.java │ ├── VpnService.java │ └── VpnServiceBinder.java ├── bin ├── android-vpn-server.apk ├── android │ ├── net │ │ └── vpn │ │ │ ├── IVpnService$Stub$Proxy.class │ │ │ ├── IVpnService$Stub.class │ │ │ ├── IVpnService.aidl │ │ │ ├── IVpnService.class │ │ │ ├── L2tpIpsecProfile.class │ │ │ ├── L2tpIpsecPskProfile.class │ │ │ ├── L2tpProfile.class │ │ │ ├── PptpProfile.class │ │ │ ├── VpnManager.class │ │ │ ├── VpnProfile$1.class │ │ │ ├── VpnProfile.aidl │ │ │ ├── VpnProfile.class │ │ │ ├── VpnState.class │ │ │ ├── VpnTest$1.class │ │ │ ├── VpnTest.class │ │ │ └── VpnType.class │ ├── os │ │ └── SystemProperties.class │ └── security │ │ ├── Credentials.class │ │ └── KeyStore.class ├── classes.dex ├── com │ └── android │ │ └── server │ │ └── vpn │ │ ├── DaemonProxy.class │ │ ├── L2tpIpsecPskService.class │ │ ├── L2tpIpsecService.class │ │ ├── L2tpService.class │ │ ├── PptpService.class │ │ ├── R$attr.class │ │ ├── R$drawable.class │ │ ├── R$layout.class │ │ ├── R$string.class │ │ ├── R.class │ │ ├── VpnConnectingError.class │ │ ├── VpnDaemons.class │ │ ├── VpnService$1.class │ │ ├── VpnService$DaemonHelper.class │ │ ├── VpnService$NotificationHelper.class │ │ ├── VpnService.class │ │ ├── VpnServiceBinder$1.class │ │ ├── VpnServiceBinder$2.class │ │ ├── VpnServiceBinder$3.class │ │ └── VpnServiceBinder.class ├── org │ └── zju │ │ └── luojs │ │ └── MyVpn.class └── resources.ap_ ├── default.properties ├── gen ├── android │ └── net │ │ └── vpn │ │ └── IVpnService.java └── com │ └── android │ └── server │ └── vpn │ └── R.java ├── proguard.cfg ├── res.tar ├── res ├── drawable │ ├── vpn_connected.png │ └── vpn_disconnected.png ├── layout │ └── main.xml ├── values-ar │ └── strings.xml ├── values-bg │ └── strings.xml ├── values-ca │ └── strings.xml ├── values-cs │ └── strings.xml ├── values-da │ └── strings.xml ├── values-de │ └── strings.xml ├── values-el │ └── strings.xml ├── values-en-rGB │ └── strings.xml ├── values-es-rUS │ └── strings.xml ├── values-es │ └── strings.xml ├── values-fa │ └── strings.xml ├── values-fi │ └── strings.xml ├── values-fr │ └── strings.xml ├── values-hr │ └── strings.xml ├── values-hu │ └── strings.xml ├── values-in │ └── strings.xml ├── values-it │ └── strings.xml ├── values-iw │ └── strings.xml ├── values-ja │ └── strings.xml ├── values-ko │ └── strings.xml ├── values-lt │ └── strings.xml ├── values-lv │ └── strings.xml ├── values-nb │ └── strings.xml ├── values-nl │ └── strings.xml ├── values-pl │ └── strings.xml ├── values-pt-rPT │ └── strings.xml ├── values-pt │ └── strings.xml ├── values-rm │ └── strings.xml ├── values-ro │ └── strings.xml ├── values-ru │ └── strings.xml ├── values-sk │ └── strings.xml ├── values-sl │ └── strings.xml ├── values-sr │ └── strings.xml ├── values-sv │ └── strings.xml ├── values-th │ └── strings.xml ├── values-tl │ └── strings.xml ├── values-tr │ └── strings.xml ├── values-uk │ └── strings.xml ├── values-vi │ └── strings.xml ├── values-zh-rCN │ └── strings.xml ├── values-zh-rTW │ └── strings.xml └── values │ └── strings.xml └── src ├── android ├── net │ └── vpn │ │ ├── IVpnService.aidl │ │ ├── L2tpIpsecProfile.java │ │ ├── L2tpIpsecPskProfile.java │ │ ├── L2tpProfile.java │ │ ├── PptpProfile.java │ │ ├── VpnManager.java │ │ ├── VpnProfile.aidl │ │ ├── VpnProfile.java │ │ ├── VpnState.java │ │ ├── VpnTest.java │ │ └── VpnType.java ├── os │ └── SystemProperties.java └── security │ ├── Credentials.java │ └── KeyStore.java ├── com └── android │ └── server │ └── vpn │ ├── DaemonProxy.java │ ├── L2tpIpsecPskService.java │ ├── L2tpIpsecService.java │ ├── L2tpService.java │ ├── PptpService.java │ ├── VpnConnectingError.java │ ├── VpnDaemons.java │ ├── VpnService.java │ └── VpnServiceBinder.java └── org └── zju └── luojs └── MyVpn.java /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /VpnServices/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH:= $(call my-dir) 2 | include $(CLEAR_VARS) 3 | 4 | LOCAL_MODULE_TAGS := optional 5 | 6 | LOCAL_SRC_FILES := $(call all-subdir-java-files) 7 | 8 | LOCAL_JAVA_LIBRARIES := 9 | 10 | LOCAL_PACKAGE_NAME := VpnServices 11 | LOCAL_CERTIFICATE := platform 12 | 13 | include $(BUILD_PACKAGE) 14 | 15 | ######################## 16 | include $(call all-makefiles-under,$(LOCAL_PATH)) 17 | -------------------------------------------------------------------------------- /VpnServices/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /VpnServices/MODULE_LICENSE_APACHE2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/VpnServices/MODULE_LICENSE_APACHE2 -------------------------------------------------------------------------------- /VpnServices/NOTICE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2005-2008, The Android Open Source Project 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | 13 | 14 | Apache License 15 | Version 2.0, January 2004 16 | http://www.apache.org/licenses/ 17 | 18 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 19 | 20 | 1. Definitions. 21 | 22 | "License" shall mean the terms and conditions for use, reproduction, 23 | and distribution as defined by Sections 1 through 9 of this document. 24 | 25 | "Licensor" shall mean the copyright owner or entity authorized by 26 | the copyright owner that is granting the License. 27 | 28 | "Legal Entity" shall mean the union of the acting entity and all 29 | other entities that control, are controlled by, or are under common 30 | control with that entity. For the purposes of this definition, 31 | "control" means (i) the power, direct or indirect, to cause the 32 | direction or management of such entity, whether by contract or 33 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 34 | outstanding shares, or (iii) beneficial ownership of such entity. 35 | 36 | "You" (or "Your") shall mean an individual or Legal Entity 37 | exercising permissions granted by this License. 38 | 39 | "Source" form shall mean the preferred form for making modifications, 40 | including but not limited to software source code, documentation 41 | source, and configuration files. 42 | 43 | "Object" form shall mean any form resulting from mechanical 44 | transformation or translation of a Source form, including but 45 | not limited to compiled object code, generated documentation, 46 | and conversions to other media types. 47 | 48 | "Work" shall mean the work of authorship, whether in Source or 49 | Object form, made available under the License, as indicated by a 50 | copyright notice that is included in or attached to the work 51 | (an example is provided in the Appendix below). 52 | 53 | "Derivative Works" shall mean any work, whether in Source or Object 54 | form, that is based on (or derived from) the Work and for which the 55 | editorial revisions, annotations, elaborations, or other modifications 56 | represent, as a whole, an original work of authorship. For the purposes 57 | of this License, Derivative Works shall not include works that remain 58 | separable from, or merely link (or bind by name) to the interfaces of, 59 | the Work and Derivative Works thereof. 60 | 61 | "Contribution" shall mean any work of authorship, including 62 | the original version of the Work and any modifications or additions 63 | to that Work or Derivative Works thereof, that is intentionally 64 | submitted to Licensor for inclusion in the Work by the copyright owner 65 | or by an individual or Legal Entity authorized to submit on behalf of 66 | the copyright owner. For the purposes of this definition, "submitted" 67 | means any form of electronic, verbal, or written communication sent 68 | to the Licensor or its representatives, including but not limited to 69 | communication on electronic mailing lists, source code control systems, 70 | and issue tracking systems that are managed by, or on behalf of, the 71 | Licensor for the purpose of discussing and improving the Work, but 72 | excluding communication that is conspicuously marked or otherwise 73 | designated in writing by the copyright owner as "Not a Contribution." 74 | 75 | "Contributor" shall mean Licensor and any individual or Legal Entity 76 | on behalf of whom a Contribution has been received by Licensor and 77 | subsequently incorporated within the Work. 78 | 79 | 2. Grant of Copyright License. Subject to the terms and conditions of 80 | this License, each Contributor hereby grants to You a perpetual, 81 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 82 | copyright license to reproduce, prepare Derivative Works of, 83 | publicly display, publicly perform, sublicense, and distribute the 84 | Work and such Derivative Works in Source or Object form. 85 | 86 | 3. Grant of Patent License. Subject to the terms and conditions of 87 | this License, each Contributor hereby grants to You a perpetual, 88 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 89 | (except as stated in this section) patent license to make, have made, 90 | use, offer to sell, sell, import, and otherwise transfer the Work, 91 | where such license applies only to those patent claims licensable 92 | by such Contributor that are necessarily infringed by their 93 | Contribution(s) alone or by combination of their Contribution(s) 94 | with the Work to which such Contribution(s) was submitted. If You 95 | institute patent litigation against any entity (including a 96 | cross-claim or counterclaim in a lawsuit) alleging that the Work 97 | or a Contribution incorporated within the Work constitutes direct 98 | or contributory patent infringement, then any patent licenses 99 | granted to You under this License for that Work shall terminate 100 | as of the date such litigation is filed. 101 | 102 | 4. Redistribution. You may reproduce and distribute copies of the 103 | Work or Derivative Works thereof in any medium, with or without 104 | modifications, and in Source or Object form, provided that You 105 | meet the following conditions: 106 | 107 | (a) You must give any other recipients of the Work or 108 | Derivative Works a copy of this License; and 109 | 110 | (b) You must cause any modified files to carry prominent notices 111 | stating that You changed the files; and 112 | 113 | (c) You must retain, in the Source form of any Derivative Works 114 | that You distribute, all copyright, patent, trademark, and 115 | attribution notices from the Source form of the Work, 116 | excluding those notices that do not pertain to any part of 117 | the Derivative Works; and 118 | 119 | (d) If the Work includes a "NOTICE" text file as part of its 120 | distribution, then any Derivative Works that You distribute must 121 | include a readable copy of the attribution notices contained 122 | within such NOTICE file, excluding those notices that do not 123 | pertain to any part of the Derivative Works, in at least one 124 | of the following places: within a NOTICE text file distributed 125 | as part of the Derivative Works; within the Source form or 126 | documentation, if provided along with the Derivative Works; or, 127 | within a display generated by the Derivative Works, if and 128 | wherever such third-party notices normally appear. The contents 129 | of the NOTICE file are for informational purposes only and 130 | do not modify the License. You may add Your own attribution 131 | notices within Derivative Works that You distribute, alongside 132 | or as an addendum to the NOTICE text from the Work, provided 133 | that such additional attribution notices cannot be construed 134 | as modifying the License. 135 | 136 | You may add Your own copyright statement to Your modifications and 137 | may provide additional or different license terms and conditions 138 | for use, reproduction, or distribution of Your modifications, or 139 | for any such Derivative Works as a whole, provided Your use, 140 | reproduction, and distribution of the Work otherwise complies with 141 | the conditions stated in this License. 142 | 143 | 5. Submission of Contributions. Unless You explicitly state otherwise, 144 | any Contribution intentionally submitted for inclusion in the Work 145 | by You to the Licensor shall be under the terms and conditions of 146 | this License, without any additional terms or conditions. 147 | Notwithstanding the above, nothing herein shall supersede or modify 148 | the terms of any separate license agreement you may have executed 149 | with Licensor regarding such Contributions. 150 | 151 | 6. Trademarks. This License does not grant permission to use the trade 152 | names, trademarks, service marks, or product names of the Licensor, 153 | except as required for reasonable and customary use in describing the 154 | origin of the Work and reproducing the content of the NOTICE file. 155 | 156 | 7. Disclaimer of Warranty. Unless required by applicable law or 157 | agreed to in writing, Licensor provides the Work (and each 158 | Contributor provides its Contributions) on an "AS IS" BASIS, 159 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 160 | implied, including, without limitation, any warranties or conditions 161 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 162 | PARTICULAR PURPOSE. You are solely responsible for determining the 163 | appropriateness of using or redistributing the Work and assume any 164 | risks associated with Your exercise of permissions under this License. 165 | 166 | 8. Limitation of Liability. In no event and under no legal theory, 167 | whether in tort (including negligence), contract, or otherwise, 168 | unless required by applicable law (such as deliberate and grossly 169 | negligent acts) or agreed to in writing, shall any Contributor be 170 | liable to You for damages, including any direct, indirect, special, 171 | incidental, or consequential damages of any character arising as a 172 | result of this License or out of the use or inability to use the 173 | Work (including but not limited to damages for loss of goodwill, 174 | work stoppage, computer failure or malfunction, or any and all 175 | other commercial damages or losses), even if such Contributor 176 | has been advised of the possibility of such damages. 177 | 178 | 9. Accepting Warranty or Additional Liability. While redistributing 179 | the Work or Derivative Works thereof, You may choose to offer, 180 | and charge a fee for, acceptance of support, warranty, indemnity, 181 | or other liability obligations and/or rights consistent with this 182 | License. However, in accepting such obligations, You may act only 183 | on Your own behalf and on Your sole responsibility, not on behalf 184 | of any other Contributor, and only if You agree to indemnify, 185 | defend, and hold each Contributor harmless for any liability 186 | incurred by, or claims asserted against, such Contributor by reason 187 | of your accepting any such warranty or additional liability. 188 | 189 | END OF TERMS AND CONDITIONS 190 | 191 | -------------------------------------------------------------------------------- /VpnServices/src/com/android/server/vpn/DaemonProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import android.net.LocalSocket; 20 | import android.net.LocalSocketAddress; 21 | import android.net.vpn.VpnManager; 22 | import android.os.SystemProperties; 23 | import android.util.Log; 24 | 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | import java.io.OutputStream; 28 | import java.io.Serializable; 29 | 30 | /** 31 | * Proxy to start, stop and interact with a VPN daemon. 32 | * The daemon is expected to accept connection through Unix domain socket. 33 | * When the proxy successfully starts the daemon, it will establish a socket 34 | * connection with the daemon, to both send commands to the daemon and receive 35 | * response and connecting error code from the daemon. 36 | */ 37 | class DaemonProxy implements Serializable { 38 | private static final long serialVersionUID = 1L; 39 | private static final boolean DBG = true; 40 | 41 | private static final int WAITING_TIME = 15; // sec 42 | 43 | private static final String SVC_STATE_CMD_PREFIX = "init.svc."; 44 | private static final String SVC_START_CMD = "ctl.start"; 45 | private static final String SVC_STOP_CMD = "ctl.stop"; 46 | private static final String SVC_STATE_RUNNING = "running"; 47 | private static final String SVC_STATE_STOPPED = "stopped"; 48 | 49 | private static final int END_OF_ARGUMENTS = 255; 50 | 51 | private String mName; 52 | private String mTag; 53 | private transient LocalSocket mControlSocket; 54 | 55 | /** 56 | * Creates a proxy of the specified daemon. 57 | * @param daemonName name of the daemon 58 | */ 59 | DaemonProxy(String daemonName) { 60 | mName = daemonName; 61 | mTag = "SProxy_" + daemonName; 62 | } 63 | 64 | String getName() { 65 | return mName; 66 | } 67 | 68 | void start() throws IOException { 69 | String svc = mName; 70 | 71 | Log.i(mTag, "Start VPN daemon: " + svc); 72 | SystemProperties.set(SVC_START_CMD, svc); 73 | 74 | if (!blockUntil(SVC_STATE_RUNNING, WAITING_TIME)) { 75 | throw new IOException("cannot start service: " + svc); 76 | } else { 77 | mControlSocket = createServiceSocket(); 78 | } 79 | } 80 | 81 | void sendCommand(String ...args) throws IOException { 82 | OutputStream out = getControlSocketOutput(); 83 | for (String arg : args) outputString(out, arg); 84 | out.write(END_OF_ARGUMENTS); 85 | out.flush(); 86 | 87 | int result = getResultFromSocket(true); 88 | if (result != args.length) { 89 | throw new IOException("socket error, result from service: " 90 | + result); 91 | } 92 | } 93 | 94 | // returns 0 if nothing is in the receive buffer 95 | int getResultFromSocket() throws IOException { 96 | return getResultFromSocket(false); 97 | } 98 | 99 | void closeControlSocket() { 100 | if (mControlSocket == null) return; 101 | try { 102 | mControlSocket.close(); 103 | } catch (IOException e) { 104 | Log.w(mTag, "close control socket", e); 105 | } finally { 106 | mControlSocket = null; 107 | } 108 | } 109 | 110 | void stop() { 111 | String svc = mName; 112 | Log.i(mTag, "Stop VPN daemon: " + svc); 113 | SystemProperties.set(SVC_STOP_CMD, svc); 114 | boolean success = blockUntil(SVC_STATE_STOPPED, 5); 115 | if (DBG) Log.d(mTag, "stopping " + svc + ", success? " + success); 116 | } 117 | 118 | boolean isStopped() { 119 | String cmd = SVC_STATE_CMD_PREFIX + mName; 120 | return SVC_STATE_STOPPED.equals(SystemProperties.get(cmd)); 121 | } 122 | 123 | private int getResultFromSocket(boolean blocking) throws IOException { 124 | LocalSocket s = mControlSocket; 125 | if (s == null) return 0; 126 | InputStream in = s.getInputStream(); 127 | if (!blocking && in.available() == 0) return 0; 128 | 129 | int data = in.read(); 130 | Log.i(mTag, "got data from control socket: " + data); 131 | 132 | return data; 133 | } 134 | 135 | private LocalSocket createServiceSocket() throws IOException { 136 | LocalSocket s = new LocalSocket(); 137 | LocalSocketAddress a = new LocalSocketAddress(mName, 138 | LocalSocketAddress.Namespace.RESERVED); 139 | 140 | // try a few times in case the service has not listen()ed 141 | IOException excp = null; 142 | for (int i = 0; i < 10; i++) { 143 | try { 144 | s.connect(a); 145 | return s; 146 | } catch (IOException e) { 147 | if (DBG) Log.d(mTag, "service not yet listen()ing; try again"); 148 | excp = e; 149 | sleep(500); 150 | } 151 | } 152 | throw excp; 153 | } 154 | 155 | private OutputStream getControlSocketOutput() throws IOException { 156 | if (mControlSocket != null) { 157 | return mControlSocket.getOutputStream(); 158 | } else { 159 | throw new IOException("no control socket available"); 160 | } 161 | } 162 | 163 | /** 164 | * Waits for the process to be in the expected state. The method returns 165 | * false if after the specified duration (in seconds), the process is still 166 | * not in the expected state. 167 | */ 168 | private boolean blockUntil(String expectedState, int waitTime) { 169 | String cmd = SVC_STATE_CMD_PREFIX + mName; 170 | int sleepTime = 200; // ms 171 | int n = waitTime * 1000 / sleepTime; 172 | for (int i = 0; i < n; i++) { 173 | if (expectedState.equals(SystemProperties.get(cmd))) { 174 | if (DBG) { 175 | Log.d(mTag, mName + " is " + expectedState + " after " 176 | + (i * sleepTime) + " msec"); 177 | } 178 | break; 179 | } 180 | sleep(sleepTime); 181 | } 182 | return expectedState.equals(SystemProperties.get(cmd)); 183 | } 184 | 185 | private void outputString(OutputStream out, String s) throws IOException { 186 | byte[] bytes = s.getBytes(); 187 | out.write(bytes.length); 188 | out.write(bytes); 189 | out.flush(); 190 | } 191 | 192 | private void sleep(int msec) { 193 | try { 194 | Thread.currentThread().sleep(msec); 195 | } catch (InterruptedException e) { 196 | throw new RuntimeException(e); 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import android.net.vpn.L2tpIpsecPskProfile; 20 | 21 | import java.io.IOException; 22 | 23 | /** 24 | * The service that manages the preshared key based L2TP-over-IPSec VPN 25 | * connection. 26 | */ 27 | class L2tpIpsecPskService extends VpnService { 28 | private static final String IPSEC = "racoon"; 29 | 30 | @Override 31 | protected void connect(String serverIp, String username, String password) 32 | throws IOException { 33 | L2tpIpsecPskProfile p = getProfile(); 34 | VpnDaemons daemons = getDaemons(); 35 | 36 | // IPSEC 37 | daemons.startIpsecForL2tp(serverIp, p.getPresharedKey()) 38 | .closeControlSocket(); 39 | 40 | sleep(2000); // 2 seconds 41 | 42 | // L2TP 43 | daemons.startL2tp(serverIp, 44 | (p.isSecretEnabled() ? p.getSecretString() : null), 45 | username, password); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /VpnServices/src/com/android/server/vpn/L2tpIpsecService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import android.net.vpn.L2tpIpsecProfile; 20 | import android.security.Credentials; 21 | 22 | import java.io.IOException; 23 | 24 | /** 25 | * The service that manages the certificate based L2TP-over-IPSec VPN connection. 26 | */ 27 | class L2tpIpsecService extends VpnService { 28 | private static final String IPSEC = "racoon"; 29 | 30 | @Override 31 | protected void connect(String serverIp, String username, String password) 32 | throws IOException { 33 | L2tpIpsecProfile p = getProfile(); 34 | VpnDaemons daemons = getDaemons(); 35 | 36 | // IPSEC 37 | DaemonProxy ipsec = daemons.startIpsecForL2tp(serverIp, 38 | Credentials.USER_PRIVATE_KEY + p.getUserCertificate(), 39 | Credentials.USER_CERTIFICATE + p.getUserCertificate(), 40 | Credentials.CA_CERTIFICATE + p.getCaCertificate()); 41 | ipsec.closeControlSocket(); 42 | 43 | sleep(2000); // 2 seconds 44 | 45 | // L2TP 46 | daemons.startL2tp(serverIp, 47 | (p.isSecretEnabled() ? p.getSecretString() : null), 48 | username, password); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /VpnServices/src/com/android/server/vpn/L2tpService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import android.net.vpn.L2tpProfile; 20 | 21 | import java.io.IOException; 22 | 23 | /** 24 | * The service that manages the L2TP VPN connection. 25 | */ 26 | class L2tpService extends VpnService { 27 | @Override 28 | protected void connect(String serverIp, String username, String password) 29 | throws IOException { 30 | L2tpProfile p = getProfile(); 31 | getDaemons().startL2tp(serverIp, 32 | (p.isSecretEnabled() ? p.getSecretString() : null), 33 | username, password); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /VpnServices/src/com/android/server/vpn/PptpService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import android.net.vpn.PptpProfile; 20 | 21 | import java.io.IOException; 22 | 23 | /** 24 | * The service that manages the PPTP VPN connection. 25 | */ 26 | class PptpService extends VpnService { 27 | @Override 28 | protected void connect(String serverIp, String username, String password) 29 | throws IOException { 30 | PptpProfile p = getProfile(); 31 | getDaemons().startPptp(serverIp, username, password, 32 | p.isEncryptionEnabled()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /VpnServices/src/com/android/server/vpn/VpnConnectingError.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import java.io.IOException; 20 | 21 | /** 22 | * Exception thrown when a connecting attempt fails. 23 | */ 24 | class VpnConnectingError extends IOException { 25 | private int mErrorCode; 26 | 27 | VpnConnectingError(int errorCode) { 28 | super("Connecting error: " + errorCode); 29 | mErrorCode = errorCode; 30 | } 31 | 32 | int getErrorCode() { 33 | return mErrorCode; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /VpnServices/src/com/android/server/vpn/VpnDaemons.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import android.util.Log; 20 | 21 | import java.io.IOException; 22 | import java.io.Serializable; 23 | import java.util.ArrayList; 24 | import java.util.Arrays; 25 | import java.util.List; 26 | 27 | /** 28 | * A helper class for managing native VPN daemons. 29 | */ 30 | class VpnDaemons implements Serializable { 31 | static final long serialVersionUID = 1L; 32 | private final String TAG = VpnDaemons.class.getSimpleName(); 33 | 34 | private static final String MTPD = "mtpd"; 35 | private static final String IPSEC = "racoon"; 36 | 37 | private static final String L2TP = "l2tp"; 38 | private static final String L2TP_PORT = "1701"; 39 | 40 | private static final String PPTP = "pptp"; 41 | private static final String PPTP_PORT = "1723"; 42 | 43 | private static final String VPN_LINKNAME = "vpn"; 44 | private static final String PPP_ARGS_SEPARATOR = ""; 45 | 46 | private List mDaemonList = new ArrayList(); 47 | 48 | public DaemonProxy startL2tp(String serverIp, String secret, 49 | String username, String password) throws IOException { 50 | return startMtpd(L2TP, serverIp, L2TP_PORT, secret, username, password, 51 | false); 52 | } 53 | 54 | public DaemonProxy startPptp(String serverIp, String username, 55 | String password, boolean encryption) throws IOException { 56 | return startMtpd(PPTP, serverIp, PPTP_PORT, null, username, password, 57 | encryption); 58 | } 59 | 60 | public DaemonProxy startIpsecForL2tp(String serverIp, String pskKey) 61 | throws IOException { 62 | DaemonProxy ipsec = startDaemon(IPSEC); 63 | ipsec.sendCommand(serverIp, L2TP_PORT, pskKey); 64 | return ipsec; 65 | } 66 | 67 | public DaemonProxy startIpsecForL2tp(String serverIp, String userKeyKey, 68 | String userCertKey, String caCertKey) throws IOException { 69 | DaemonProxy ipsec = startDaemon(IPSEC); 70 | ipsec.sendCommand(serverIp, L2TP_PORT, userKeyKey, userCertKey, 71 | caCertKey); 72 | return ipsec; 73 | } 74 | 75 | public synchronized void stopAll() { 76 | new DaemonProxy(MTPD).stop(); 77 | new DaemonProxy(IPSEC).stop(); 78 | } 79 | 80 | public synchronized void closeSockets() { 81 | for (DaemonProxy s : mDaemonList) s.closeControlSocket(); 82 | } 83 | 84 | public synchronized boolean anyDaemonStopped() { 85 | for (DaemonProxy s : mDaemonList) { 86 | if (s.isStopped()) { 87 | Log.w(TAG, " VPN daemon gone: " + s.getName()); 88 | return true; 89 | } 90 | } 91 | return false; 92 | } 93 | 94 | public synchronized int getSocketError() { 95 | for (DaemonProxy s : mDaemonList) { 96 | int errCode = getResultFromSocket(s); 97 | if (errCode != 0) return errCode; 98 | } 99 | return 0; 100 | } 101 | 102 | private synchronized DaemonProxy startDaemon(String daemonName) 103 | throws IOException { 104 | DaemonProxy daemon = new DaemonProxy(daemonName); 105 | mDaemonList.add(daemon); 106 | daemon.start(); 107 | return daemon; 108 | } 109 | 110 | private int getResultFromSocket(DaemonProxy s) { 111 | try { 112 | return s.getResultFromSocket(); 113 | } catch (IOException e) { 114 | return -1; 115 | } 116 | } 117 | 118 | private DaemonProxy startMtpd(String protocol, 119 | String serverIp, String port, String secret, String username, 120 | String password, boolean encryption) throws IOException { 121 | ArrayList args = new ArrayList(); 122 | args.addAll(Arrays.asList(protocol, serverIp, port)); 123 | if (secret != null) args.add(secret); 124 | args.add(PPP_ARGS_SEPARATOR); 125 | addPppArguments(args, serverIp, username, password, encryption); 126 | 127 | DaemonProxy mtpd = startDaemon(MTPD); 128 | mtpd.sendCommand(args.toArray(new String[args.size()])); 129 | return mtpd; 130 | } 131 | 132 | private static void addPppArguments(ArrayList args, String serverIp, 133 | String username, String password, boolean encryption) 134 | throws IOException { 135 | args.addAll(Arrays.asList( 136 | "linkname", VPN_LINKNAME, 137 | "name", username, 138 | "password", password, 139 | "refuse-eap", "nodefaultroute", "usepeerdns", 140 | "idle", "1800", 141 | "mtu", "1400", 142 | "mru", "1400")); 143 | if (encryption) { 144 | args.add("+mppe"); 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /VpnServices/src/com/android/server/vpn/VpnService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import android.app.Notification; 20 | import android.app.NotificationManager; 21 | import android.app.PendingIntent; 22 | import android.content.Context; 23 | import android.net.vpn.VpnManager; 24 | import android.net.vpn.VpnProfile; 25 | import android.net.vpn.VpnState; 26 | import android.os.SystemProperties; 27 | import android.text.TextUtils; 28 | import android.util.Log; 29 | 30 | import java.io.IOException; 31 | import java.io.Serializable; 32 | import java.net.DatagramSocket; 33 | import java.net.InetAddress; 34 | import java.net.NetworkInterface; 35 | import java.net.UnknownHostException; 36 | 37 | /** 38 | * The service base class for managing a type of VPN connection. 39 | */ 40 | abstract class VpnService implements Serializable { 41 | static final long serialVersionUID = 1L; 42 | private static final boolean DBG = true; 43 | private static final int NOTIFICATION_ID = 1; 44 | 45 | private static final String DNS1 = "net.dns1"; 46 | private static final String DNS2 = "net.dns2"; 47 | private static final String VPN_DNS1 = "vpn.dns1"; 48 | private static final String VPN_DNS2 = "vpn.dns2"; 49 | private static final String VPN_STATUS = "vpn.status"; 50 | private static final String VPN_IS_UP = "ok"; 51 | private static final String VPN_IS_DOWN = "down"; 52 | 53 | private static final String REMOTE_IP = "net.ipremote"; 54 | private static final String DNS_DOMAIN_SUFFICES = "net.dns.search"; 55 | 56 | private final String TAG = VpnService.class.getSimpleName(); 57 | 58 | // FIXME: profile is only needed in connecting phase, so we can just save 59 | // the profile name and service class name for recovery 60 | E mProfile; 61 | transient VpnServiceBinder mContext; 62 | 63 | private VpnState mState = VpnState.IDLE; 64 | private Throwable mError; 65 | 66 | // connection settings 67 | private String mOriginalDns1; 68 | private String mOriginalDns2; 69 | private String mOriginalDomainSuffices; 70 | private String mLocalIp; 71 | private String mLocalIf; 72 | 73 | private long mStartTime; // VPN connection start time 74 | 75 | // for helping managing daemons 76 | private VpnDaemons mDaemons = new VpnDaemons(); 77 | 78 | // for helping showing, updating notification 79 | private transient NotificationHelper mNotification; 80 | 81 | /** 82 | * Establishes a VPN connection with the specified username and password. 83 | */ 84 | protected abstract void connect(String serverIp, String username, 85 | String password) throws IOException; 86 | 87 | /** 88 | * Returns the daemons management class for this service object. 89 | */ 90 | protected VpnDaemons getDaemons() { 91 | return mDaemons; 92 | } 93 | 94 | /** 95 | * Returns the VPN profile associated with the connection. 96 | */ 97 | protected E getProfile() { 98 | return mProfile; 99 | } 100 | 101 | /** 102 | * Returns the IP address of the specified host name. 103 | */ 104 | protected String getIp(String hostName) throws IOException { 105 | return InetAddress.getByName(hostName).getHostAddress(); 106 | } 107 | 108 | void setContext(VpnServiceBinder context, E profile) { 109 | mProfile = profile; 110 | recover(context); 111 | } 112 | 113 | void recover(VpnServiceBinder context) { 114 | mContext = context; 115 | mNotification = new NotificationHelper(); 116 | 117 | if (VpnState.CONNECTED.equals(mState)) { 118 | Log.i("VpnService", " recovered: " + mProfile.getName()); 119 | startConnectivityMonitor(); 120 | } 121 | } 122 | 123 | VpnState getState() { 124 | return mState; 125 | } 126 | 127 | synchronized boolean onConnect(String username, String password) { 128 | try { 129 | setState(VpnState.CONNECTING); 130 | 131 | mDaemons.stopAll(); 132 | String serverIp = getIp(getProfile().getServerName()); 133 | saveLocalIpAndInterface(serverIp); 134 | onBeforeConnect(); 135 | connect(serverIp, username, password); 136 | waitUntilConnectedOrTimedout(); 137 | return true; 138 | } catch (Throwable e) { 139 | onError(e); 140 | return false; 141 | } 142 | } 143 | 144 | synchronized void onDisconnect() { 145 | try { 146 | Log.i(TAG, "disconnecting VPN..."); 147 | setState(VpnState.DISCONNECTING); 148 | mNotification.showDisconnect(); 149 | 150 | mDaemons.stopAll(); 151 | } catch (Throwable e) { 152 | Log.e(TAG, "onDisconnect()", e); 153 | } finally { 154 | onFinalCleanUp(); 155 | } 156 | } 157 | 158 | private void onError(Throwable error) { 159 | // error may occur during or after connection setup 160 | // and it may be due to one or all services gone 161 | if (mError != null) { 162 | Log.w(TAG, " multiple errors occur, record the last one: " 163 | + error); 164 | } 165 | Log.e(TAG, "onError()", error); 166 | mError = error; 167 | onDisconnect(); 168 | } 169 | 170 | private void onError(int errorCode) { 171 | onError(new VpnConnectingError(errorCode)); 172 | } 173 | 174 | 175 | private void onBeforeConnect() throws IOException { 176 | mNotification.disableNotification(); 177 | 178 | SystemProperties.set(VPN_DNS1, ""); 179 | SystemProperties.set(VPN_DNS2, ""); 180 | SystemProperties.set(VPN_STATUS, VPN_IS_DOWN); 181 | if (DBG) { 182 | Log.d(TAG, " VPN UP: " + SystemProperties.get(VPN_STATUS)); 183 | } 184 | } 185 | 186 | private void waitUntilConnectedOrTimedout() throws IOException { 187 | sleep(2000); // 2 seconds 188 | for (int i = 0; i < 80; i++) { 189 | if (mState != VpnState.CONNECTING) { 190 | break; 191 | } else if (VPN_IS_UP.equals( 192 | SystemProperties.get(VPN_STATUS))) { 193 | onConnected(); 194 | return; 195 | } else { 196 | int err = mDaemons.getSocketError(); 197 | if (err != 0) { 198 | onError(err); 199 | return; 200 | } 201 | } 202 | sleep(500); // 0.5 second 203 | } 204 | 205 | if (mState == VpnState.CONNECTING) { 206 | onError(new IOException("Connecting timed out")); 207 | } 208 | } 209 | 210 | private synchronized void onConnected() throws IOException { 211 | if (DBG) Log.d(TAG, "onConnected()"); 212 | 213 | mDaemons.closeSockets(); 214 | saveOriginalDns(); 215 | saveAndSetDomainSuffices(); 216 | 217 | mStartTime = System.currentTimeMillis(); 218 | 219 | // Correct order to make sure VpnService doesn't break when killed: 220 | // (1) set state to CONNECTED 221 | // (2) save states 222 | // (3) set DNS 223 | setState(VpnState.CONNECTED); 224 | saveSelf(); 225 | setVpnDns(); 226 | 227 | startConnectivityMonitor(); 228 | } 229 | 230 | private void saveSelf() throws IOException { 231 | mContext.saveStates(); 232 | } 233 | 234 | private synchronized void onFinalCleanUp() { 235 | if (DBG) Log.d(TAG, "onFinalCleanUp()"); 236 | 237 | if (mState == VpnState.IDLE) return; 238 | 239 | // keep the notification when error occurs 240 | if (!anyError()) mNotification.disableNotification(); 241 | 242 | restoreOriginalDns(); 243 | restoreOriginalDomainSuffices(); 244 | setState(VpnState.IDLE); 245 | 246 | // stop the service itself 247 | SystemProperties.set(VPN_STATUS, VPN_IS_DOWN); 248 | mContext.removeStates(); 249 | mContext.stopSelf(); 250 | } 251 | 252 | private boolean anyError() { 253 | return (mError != null); 254 | } 255 | 256 | private void restoreOriginalDns() { 257 | // restore only if they are not overridden 258 | String vpnDns1 = SystemProperties.get(VPN_DNS1); 259 | if (vpnDns1.equals(SystemProperties.get(DNS1))) { 260 | Log.i(TAG, String.format("restore original dns prop: %s --> %s", 261 | SystemProperties.get(DNS1), mOriginalDns1)); 262 | Log.i(TAG, String.format("restore original dns prop: %s --> %s", 263 | SystemProperties.get(DNS2), mOriginalDns2)); 264 | SystemProperties.set(DNS1, mOriginalDns1); 265 | SystemProperties.set(DNS2, mOriginalDns2); 266 | } 267 | } 268 | 269 | private void saveOriginalDns() { 270 | mOriginalDns1 = SystemProperties.get(DNS1); 271 | mOriginalDns2 = SystemProperties.get(DNS2); 272 | Log.i(TAG, String.format("save original dns prop: %s, %s", 273 | mOriginalDns1, mOriginalDns2)); 274 | } 275 | 276 | private void setVpnDns() { 277 | String vpnDns1 = SystemProperties.get(VPN_DNS1); 278 | String vpnDns2 = SystemProperties.get(VPN_DNS2); 279 | SystemProperties.set(DNS1, vpnDns1); 280 | SystemProperties.set(DNS2, vpnDns2); 281 | Log.i(TAG, String.format("set vpn dns prop: %s, %s", 282 | vpnDns1, vpnDns2)); 283 | } 284 | 285 | private void saveAndSetDomainSuffices() { 286 | mOriginalDomainSuffices = SystemProperties.get(DNS_DOMAIN_SUFFICES); 287 | Log.i(TAG, "save original suffices: " + mOriginalDomainSuffices); 288 | String list = mProfile.getDomainSuffices(); 289 | if (!TextUtils.isEmpty(list)) { 290 | SystemProperties.set(DNS_DOMAIN_SUFFICES, list); 291 | } 292 | } 293 | 294 | private void restoreOriginalDomainSuffices() { 295 | Log.i(TAG, "restore original suffices --> " + mOriginalDomainSuffices); 296 | SystemProperties.set(DNS_DOMAIN_SUFFICES, mOriginalDomainSuffices); 297 | } 298 | 299 | private void setState(VpnState newState) { 300 | mState = newState; 301 | broadcastConnectivity(newState); 302 | } 303 | 304 | private void broadcastConnectivity(VpnState s) { 305 | VpnManager m = new VpnManager(mContext); 306 | Throwable err = mError; 307 | if ((s == VpnState.IDLE) && (err != null)) { 308 | if (err instanceof UnknownHostException) { 309 | m.broadcastConnectivity(mProfile.getName(), s, 310 | VpnManager.VPN_ERROR_UNKNOWN_SERVER); 311 | } else if (err instanceof VpnConnectingError) { 312 | m.broadcastConnectivity(mProfile.getName(), s, 313 | ((VpnConnectingError) err).getErrorCode()); 314 | } else if (VPN_IS_UP.equals(SystemProperties.get(VPN_STATUS))) { 315 | m.broadcastConnectivity(mProfile.getName(), s, 316 | VpnManager.VPN_ERROR_CONNECTION_LOST); 317 | } else { 318 | m.broadcastConnectivity(mProfile.getName(), s, 319 | VpnManager.VPN_ERROR_CONNECTION_FAILED); 320 | } 321 | } else { 322 | m.broadcastConnectivity(mProfile.getName(), s); 323 | } 324 | } 325 | 326 | private void startConnectivityMonitor() { 327 | new Thread(new Runnable() { 328 | public void run() { 329 | Log.i(TAG, "VPN connectivity monitor running"); 330 | try { 331 | for (int i = 10; ; i--) { 332 | long now = System.currentTimeMillis(); 333 | 334 | boolean heavyCheck = i == 0; 335 | synchronized (VpnService.this) { 336 | if (mState != VpnState.CONNECTED) break; 337 | mNotification.update(now); 338 | 339 | if (heavyCheck) { 340 | i = 10; 341 | if (checkConnectivity()) checkDns(); 342 | } 343 | long t = 1000L - System.currentTimeMillis() + now; 344 | if (t > 100L) VpnService.this.wait(t); 345 | } 346 | } 347 | } catch (InterruptedException e) { 348 | onError(e); 349 | } 350 | Log.i(TAG, "VPN connectivity monitor stopped"); 351 | } 352 | }).start(); 353 | } 354 | 355 | private void saveLocalIpAndInterface(String serverIp) throws IOException { 356 | DatagramSocket s = new DatagramSocket(); 357 | int port = 80; // arbitrary 358 | s.connect(InetAddress.getByName(serverIp), port); 359 | InetAddress localIp = s.getLocalAddress(); 360 | mLocalIp = localIp.getHostAddress(); 361 | NetworkInterface localIf = NetworkInterface.getByInetAddress(localIp); 362 | mLocalIf = (localIf == null) ? null : localIf.getName(); 363 | if (TextUtils.isEmpty(mLocalIf)) { 364 | throw new IOException("Local interface is empty!"); 365 | } 366 | if (DBG) { 367 | Log.d(TAG, " Local IP: " + mLocalIp + ", if: " + mLocalIf); 368 | } 369 | } 370 | 371 | // returns false if vpn connectivity is broken 372 | private boolean checkConnectivity() { 373 | if (mDaemons.anyDaemonStopped() || isLocalIpChanged()) { 374 | onError(new IOException("Connectivity lost")); 375 | return false; 376 | } else { 377 | return true; 378 | } 379 | } 380 | 381 | private void checkDns() { 382 | String dns1 = SystemProperties.get(DNS1); 383 | String vpnDns1 = SystemProperties.get(VPN_DNS1); 384 | if (!dns1.equals(vpnDns1) && dns1.equals(mOriginalDns1)) { 385 | // dhcp expires? 386 | setVpnDns(); 387 | } 388 | } 389 | 390 | private boolean isLocalIpChanged() { 391 | try { 392 | InetAddress localIp = InetAddress.getByName(mLocalIp); 393 | NetworkInterface localIf = 394 | NetworkInterface.getByInetAddress(localIp); 395 | if (localIf == null || !mLocalIf.equals(localIf.getName())) { 396 | Log.w(TAG, " local If changed from " + mLocalIf 397 | + " to " + localIf); 398 | return true; 399 | } else { 400 | return false; 401 | } 402 | } catch (IOException e) { 403 | Log.w(TAG, "isLocalIpChanged()", e); 404 | return true; 405 | } 406 | } 407 | 408 | protected void sleep(int ms) { 409 | try { 410 | Thread.currentThread().sleep(ms); 411 | } catch (InterruptedException e) { 412 | } 413 | } 414 | 415 | private class DaemonHelper implements Serializable { 416 | } 417 | 418 | // Helper class for showing, updating notification. 419 | private class NotificationHelper { 420 | void update(long now) { 421 | String title = getNotificationTitle(true); 422 | Notification n = new Notification(R.drawable.vpn_connected, title, 423 | mStartTime); 424 | n.setLatestEventInfo(mContext, title, 425 | getConnectedNotificationMessage(now), 426 | prepareNotificationIntent()); 427 | n.flags |= Notification.FLAG_NO_CLEAR; 428 | n.flags |= Notification.FLAG_ONGOING_EVENT; 429 | enableNotification(n); 430 | } 431 | 432 | void showDisconnect() { 433 | String title = getNotificationTitle(false); 434 | Notification n = new Notification(R.drawable.vpn_disconnected, 435 | title, System.currentTimeMillis()); 436 | n.setLatestEventInfo(mContext, title, 437 | getDisconnectedNotificationMessage(), 438 | prepareNotificationIntent()); 439 | n.flags |= Notification.FLAG_AUTO_CANCEL; 440 | disableNotification(); 441 | enableNotification(n); 442 | } 443 | 444 | void disableNotification() { 445 | ((NotificationManager) mContext.getSystemService( 446 | Context.NOTIFICATION_SERVICE)).cancel(NOTIFICATION_ID); 447 | } 448 | 449 | private void enableNotification(Notification n) { 450 | ((NotificationManager) mContext.getSystemService( 451 | Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, n); 452 | } 453 | 454 | private PendingIntent prepareNotificationIntent() { 455 | return PendingIntent.getActivity(mContext, 0, 456 | new VpnManager(mContext).createSettingsActivityIntent(), 0); 457 | } 458 | 459 | private String getNotificationTitle(boolean connected) { 460 | String formatString = connected 461 | ? mContext.getString( 462 | R.string.vpn_notification_title_connected) 463 | : mContext.getString( 464 | R.string.vpn_notification_title_disconnected); 465 | return String.format(formatString, mProfile.getName()); 466 | } 467 | 468 | private String getFormattedTime(int duration) { 469 | int hours = duration / 3600; 470 | StringBuilder sb = new StringBuilder(); 471 | if (hours > 0) sb.append(hours).append(':'); 472 | sb.append(String.format("%02d:%02d", (duration % 3600 / 60), 473 | (duration % 60))); 474 | return sb.toString(); 475 | } 476 | 477 | private String getConnectedNotificationMessage(long now) { 478 | return getFormattedTime((int) (now - mStartTime) / 1000); 479 | } 480 | 481 | private String getDisconnectedNotificationMessage() { 482 | return mContext.getString( 483 | R.string.vpn_notification_hint_disconnected); 484 | } 485 | } 486 | } 487 | -------------------------------------------------------------------------------- /VpnServices/src/com/android/server/vpn/VpnServiceBinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import android.app.Service; 20 | import android.content.Intent; 21 | import android.net.vpn.IVpnService; 22 | import android.net.vpn.L2tpIpsecProfile; 23 | import android.net.vpn.L2tpIpsecPskProfile; 24 | import android.net.vpn.L2tpProfile; 25 | import android.net.vpn.PptpProfile; 26 | import android.net.vpn.VpnManager; 27 | import android.net.vpn.VpnProfile; 28 | import android.net.vpn.VpnState; 29 | import android.os.Environment; 30 | import android.os.IBinder; 31 | import android.os.SystemProperties; 32 | import android.util.Log; 33 | 34 | import java.io.File; 35 | import java.io.FileInputStream; 36 | import java.io.FileNotFoundException; 37 | import java.io.FileOutputStream; 38 | import java.io.IOException; 39 | import java.io.ObjectInputStream; 40 | import java.io.ObjectOutputStream; 41 | 42 | /** 43 | * The service class for managing a VPN connection. It implements the 44 | * {@link IVpnService} binder interface. 45 | */ 46 | public class VpnServiceBinder extends Service { 47 | private static final String TAG = VpnServiceBinder.class.getSimpleName(); 48 | private static final boolean DBG = true; 49 | 50 | private static final String STATES_FILE_RELATIVE_PATH = "/misc/vpn/.states"; 51 | 52 | // The actual implementation is delegated to the VpnService class. 53 | private VpnService mService; 54 | 55 | // TODO(oam): Test VPN when EFS is enabled (will do later)... 56 | private static String getStateFilePath() { 57 | // This call will return the correcu directory whether Encrypted FS is enabled or not 58 | // Disabled: /data/misc/vpn/.states Enabled: /data/secure/misc/vpn/.states 59 | return Environment.getSecureDataDirectory().getPath() + STATES_FILE_RELATIVE_PATH; 60 | } 61 | 62 | private final IBinder mBinder = new IVpnService.Stub() { 63 | public boolean connect(VpnProfile p, String username, String password) { 64 | return VpnServiceBinder.this.connect(p, username, password); 65 | } 66 | 67 | public void disconnect() { 68 | VpnServiceBinder.this.disconnect(); 69 | } 70 | 71 | public void checkStatus(VpnProfile p) { 72 | VpnServiceBinder.this.checkStatus(p); 73 | } 74 | }; 75 | 76 | @Override 77 | public void onCreate() { 78 | super.onCreate(); 79 | checkSavedStates(); 80 | } 81 | 82 | 83 | @Override 84 | public void onStart(Intent intent, int startId) { 85 | super.onStart(intent, startId); 86 | } 87 | 88 | @Override 89 | public IBinder onBind(Intent intent) { 90 | return mBinder; 91 | } 92 | 93 | void saveStates() throws IOException { 94 | if (DBG) Log.d("VpnServiceBinder", " saving states"); 95 | ObjectOutputStream oos = 96 | new ObjectOutputStream(new FileOutputStream(getStateFilePath())); 97 | oos.writeObject(mService); 98 | oos.close(); 99 | } 100 | 101 | void removeStates() { 102 | try { 103 | File f = new File(getStateFilePath()); 104 | if (f.exists()) f.delete(); 105 | } catch (Throwable e) { 106 | if (DBG) Log.d("VpnServiceBinder", " remove states: " + e); 107 | } 108 | } 109 | 110 | private synchronized boolean connect(final VpnProfile p, 111 | final String username, final String password) { 112 | if (mService != null) return false; 113 | final VpnService s = mService = createService(p); 114 | 115 | new Thread(new Runnable() { 116 | public void run() { 117 | s.onConnect(username, password); 118 | } 119 | }).start(); 120 | return true; 121 | } 122 | 123 | private synchronized void disconnect() { 124 | if (mService == null) return; 125 | final VpnService s = mService; 126 | 127 | new Thread(new Runnable() { 128 | public void run() { 129 | s.onDisconnect(); 130 | } 131 | }).start(); 132 | } 133 | 134 | private synchronized void checkStatus(VpnProfile p) { 135 | if ((mService == null) 136 | || (!p.getName().equals(mService.mProfile.getName()))) { 137 | broadcastConnectivity(p.getName(), VpnState.IDLE); 138 | } else { 139 | broadcastConnectivity(p.getName(), mService.getState()); 140 | } 141 | } 142 | 143 | private void checkSavedStates() { 144 | try { 145 | ObjectInputStream ois = new ObjectInputStream(new FileInputStream( 146 | getStateFilePath())); 147 | mService = (VpnService) ois.readObject(); 148 | mService.recover(this); 149 | ois.close(); 150 | } catch (FileNotFoundException e) { 151 | // do nothing 152 | } catch (Throwable e) { 153 | Log.i("VpnServiceBinder", "recovery error, remove states: " + e); 154 | removeStates(); 155 | } 156 | } 157 | 158 | private VpnService createService(VpnProfile p) { 159 | switch (p.getType()) { 160 | case L2TP: 161 | L2tpService l2tp = new L2tpService(); 162 | l2tp.setContext(this, (L2tpProfile) p); 163 | return l2tp; 164 | 165 | case PPTP: 166 | PptpService pptp = new PptpService(); 167 | pptp.setContext(this, (PptpProfile) p); 168 | return pptp; 169 | 170 | case L2TP_IPSEC_PSK: 171 | L2tpIpsecPskService psk = new L2tpIpsecPskService(); 172 | psk.setContext(this, (L2tpIpsecPskProfile) p); 173 | return psk; 174 | 175 | case L2TP_IPSEC: 176 | L2tpIpsecService l2tpIpsec = new L2tpIpsecService(); 177 | l2tpIpsec.setContext(this, (L2tpIpsecProfile) p); 178 | return l2tpIpsec; 179 | 180 | default: 181 | return null; 182 | } 183 | } 184 | 185 | private void broadcastConnectivity(String name, VpnState s) { 186 | new VpnManager(this).broadcastConnectivity(name, s); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /bin/android-vpn-server.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android-vpn-server.apk -------------------------------------------------------------------------------- /bin/android/net/vpn/IVpnService$Stub$Proxy.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/net/vpn/IVpnService$Stub$Proxy.class -------------------------------------------------------------------------------- /bin/android/net/vpn/IVpnService$Stub.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/net/vpn/IVpnService$Stub.class -------------------------------------------------------------------------------- /bin/android/net/vpn/IVpnService.aidl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.net.vpn; 18 | 19 | import android.net.vpn.VpnProfile; 20 | 21 | /** 22 | * Interface to access a VPN service. 23 | * {@hide} 24 | */ 25 | interface IVpnService { 26 | /** 27 | * Sets up the VPN connection. 28 | * @param profile the profile object 29 | * @param username the username for authentication 30 | * @param password the corresponding password for authentication 31 | */ 32 | boolean connect(in VpnProfile profile, String username, String password); 33 | 34 | /** 35 | * Tears down the VPN connection. 36 | */ 37 | void disconnect(); 38 | 39 | /** 40 | * Makes the service broadcast the connectivity state. 41 | */ 42 | void checkStatus(in VpnProfile profile); 43 | } 44 | -------------------------------------------------------------------------------- /bin/android/net/vpn/IVpnService.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/net/vpn/IVpnService.class -------------------------------------------------------------------------------- /bin/android/net/vpn/L2tpIpsecProfile.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/net/vpn/L2tpIpsecProfile.class -------------------------------------------------------------------------------- /bin/android/net/vpn/L2tpIpsecPskProfile.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/net/vpn/L2tpIpsecPskProfile.class -------------------------------------------------------------------------------- /bin/android/net/vpn/L2tpProfile.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/net/vpn/L2tpProfile.class -------------------------------------------------------------------------------- /bin/android/net/vpn/PptpProfile.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/net/vpn/PptpProfile.class -------------------------------------------------------------------------------- /bin/android/net/vpn/VpnManager.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/net/vpn/VpnManager.class -------------------------------------------------------------------------------- /bin/android/net/vpn/VpnProfile$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/net/vpn/VpnProfile$1.class -------------------------------------------------------------------------------- /bin/android/net/vpn/VpnProfile.aidl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.net.vpn; 18 | 19 | parcelable VpnProfile; 20 | -------------------------------------------------------------------------------- /bin/android/net/vpn/VpnProfile.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/net/vpn/VpnProfile.class -------------------------------------------------------------------------------- /bin/android/net/vpn/VpnState.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/net/vpn/VpnState.class -------------------------------------------------------------------------------- /bin/android/net/vpn/VpnTest$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/net/vpn/VpnTest$1.class -------------------------------------------------------------------------------- /bin/android/net/vpn/VpnTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/net/vpn/VpnTest.class -------------------------------------------------------------------------------- /bin/android/net/vpn/VpnType.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/net/vpn/VpnType.class -------------------------------------------------------------------------------- /bin/android/os/SystemProperties.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/os/SystemProperties.class -------------------------------------------------------------------------------- /bin/android/security/Credentials.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/security/Credentials.class -------------------------------------------------------------------------------- /bin/android/security/KeyStore.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/android/security/KeyStore.class -------------------------------------------------------------------------------- /bin/classes.dex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/classes.dex -------------------------------------------------------------------------------- /bin/com/android/server/vpn/DaemonProxy.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/DaemonProxy.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/L2tpIpsecPskService.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/L2tpIpsecPskService.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/L2tpIpsecService.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/L2tpIpsecService.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/L2tpService.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/L2tpService.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/PptpService.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/PptpService.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/R$attr.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/R$attr.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/R$drawable.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/R$drawable.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/R$layout.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/R$layout.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/R$string.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/R$string.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/R.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/R.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/VpnConnectingError.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/VpnConnectingError.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/VpnDaemons.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/VpnDaemons.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/VpnService$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/VpnService$1.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/VpnService$DaemonHelper.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/VpnService$DaemonHelper.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/VpnService$NotificationHelper.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/VpnService$NotificationHelper.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/VpnService.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/VpnService.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/VpnServiceBinder$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/VpnServiceBinder$1.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/VpnServiceBinder$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/VpnServiceBinder$2.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/VpnServiceBinder$3.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/VpnServiceBinder$3.class -------------------------------------------------------------------------------- /bin/com/android/server/vpn/VpnServiceBinder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/com/android/server/vpn/VpnServiceBinder.class -------------------------------------------------------------------------------- /bin/org/zju/luojs/MyVpn.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/org/zju/luojs/MyVpn.class -------------------------------------------------------------------------------- /bin/resources.ap_: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/bin/resources.ap_ -------------------------------------------------------------------------------- /default.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system use, 7 | # "build.properties", and override values to adapt the script to your 8 | # project structure. 9 | 10 | # Project target. 11 | target=android-8 12 | -------------------------------------------------------------------------------- /gen/android/net/vpn/IVpnService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is auto-generated. DO NOT MODIFY. 3 | * Original file: /home/luojiesi/program/android-vpn-server/src/android/net/vpn/IVpnService.aidl 4 | */ 5 | package android.net.vpn; 6 | /** 7 | * Interface to access a VPN service. 8 | * {@hide} 9 | */ 10 | public interface IVpnService extends android.os.IInterface 11 | { 12 | /** Local-side IPC implementation stub class. */ 13 | public static abstract class Stub extends android.os.Binder implements android.net.vpn.IVpnService 14 | { 15 | private static final java.lang.String DESCRIPTOR = "android.net.vpn.IVpnService"; 16 | /** Construct the stub at attach it to the interface. */ 17 | public Stub() 18 | { 19 | this.attachInterface(this, DESCRIPTOR); 20 | } 21 | /** 22 | * Cast an IBinder object into an android.net.vpn.IVpnService interface, 23 | * generating a proxy if needed. 24 | */ 25 | public static android.net.vpn.IVpnService asInterface(android.os.IBinder obj) 26 | { 27 | if ((obj==null)) { 28 | return null; 29 | } 30 | android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR); 31 | if (((iin!=null)&&(iin instanceof android.net.vpn.IVpnService))) { 32 | return ((android.net.vpn.IVpnService)iin); 33 | } 34 | return new android.net.vpn.IVpnService.Stub.Proxy(obj); 35 | } 36 | public android.os.IBinder asBinder() 37 | { 38 | return this; 39 | } 40 | @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException 41 | { 42 | switch (code) 43 | { 44 | case INTERFACE_TRANSACTION: 45 | { 46 | reply.writeString(DESCRIPTOR); 47 | return true; 48 | } 49 | case TRANSACTION_connect: 50 | { 51 | data.enforceInterface(DESCRIPTOR); 52 | android.net.vpn.VpnProfile _arg0; 53 | if ((0!=data.readInt())) { 54 | _arg0 = android.net.vpn.VpnProfile.CREATOR.createFromParcel(data); 55 | } 56 | else { 57 | _arg0 = null; 58 | } 59 | java.lang.String _arg1; 60 | _arg1 = data.readString(); 61 | java.lang.String _arg2; 62 | _arg2 = data.readString(); 63 | boolean _result = this.connect(_arg0, _arg1, _arg2); 64 | reply.writeNoException(); 65 | reply.writeInt(((_result)?(1):(0))); 66 | return true; 67 | } 68 | case TRANSACTION_disconnect: 69 | { 70 | data.enforceInterface(DESCRIPTOR); 71 | this.disconnect(); 72 | reply.writeNoException(); 73 | return true; 74 | } 75 | case TRANSACTION_checkStatus: 76 | { 77 | data.enforceInterface(DESCRIPTOR); 78 | android.net.vpn.VpnProfile _arg0; 79 | if ((0!=data.readInt())) { 80 | _arg0 = android.net.vpn.VpnProfile.CREATOR.createFromParcel(data); 81 | } 82 | else { 83 | _arg0 = null; 84 | } 85 | this.checkStatus(_arg0); 86 | reply.writeNoException(); 87 | return true; 88 | } 89 | } 90 | return super.onTransact(code, data, reply, flags); 91 | } 92 | private static class Proxy implements android.net.vpn.IVpnService 93 | { 94 | private android.os.IBinder mRemote; 95 | Proxy(android.os.IBinder remote) 96 | { 97 | mRemote = remote; 98 | } 99 | public android.os.IBinder asBinder() 100 | { 101 | return mRemote; 102 | } 103 | public java.lang.String getInterfaceDescriptor() 104 | { 105 | return DESCRIPTOR; 106 | } 107 | /** 108 | * Sets up the VPN connection. 109 | * @param profile the profile object 110 | * @param username the username for authentication 111 | * @param password the corresponding password for authentication 112 | */ 113 | public boolean connect(android.net.vpn.VpnProfile profile, java.lang.String username, java.lang.String password) throws android.os.RemoteException 114 | { 115 | android.os.Parcel _data = android.os.Parcel.obtain(); 116 | android.os.Parcel _reply = android.os.Parcel.obtain(); 117 | boolean _result; 118 | try { 119 | _data.writeInterfaceToken(DESCRIPTOR); 120 | if ((profile!=null)) { 121 | _data.writeInt(1); 122 | profile.writeToParcel(_data, 0); 123 | } 124 | else { 125 | _data.writeInt(0); 126 | } 127 | _data.writeString(username); 128 | _data.writeString(password); 129 | mRemote.transact(Stub.TRANSACTION_connect, _data, _reply, 0); 130 | _reply.readException(); 131 | _result = (0!=_reply.readInt()); 132 | } 133 | finally { 134 | _reply.recycle(); 135 | _data.recycle(); 136 | } 137 | return _result; 138 | } 139 | /** 140 | * Tears down the VPN connection. 141 | */ 142 | public void disconnect() throws android.os.RemoteException 143 | { 144 | android.os.Parcel _data = android.os.Parcel.obtain(); 145 | android.os.Parcel _reply = android.os.Parcel.obtain(); 146 | try { 147 | _data.writeInterfaceToken(DESCRIPTOR); 148 | mRemote.transact(Stub.TRANSACTION_disconnect, _data, _reply, 0); 149 | _reply.readException(); 150 | } 151 | finally { 152 | _reply.recycle(); 153 | _data.recycle(); 154 | } 155 | } 156 | /** 157 | * Makes the service broadcast the connectivity state. 158 | */ 159 | public void checkStatus(android.net.vpn.VpnProfile profile) throws android.os.RemoteException 160 | { 161 | android.os.Parcel _data = android.os.Parcel.obtain(); 162 | android.os.Parcel _reply = android.os.Parcel.obtain(); 163 | try { 164 | _data.writeInterfaceToken(DESCRIPTOR); 165 | if ((profile!=null)) { 166 | _data.writeInt(1); 167 | profile.writeToParcel(_data, 0); 168 | } 169 | else { 170 | _data.writeInt(0); 171 | } 172 | mRemote.transact(Stub.TRANSACTION_checkStatus, _data, _reply, 0); 173 | _reply.readException(); 174 | } 175 | finally { 176 | _reply.recycle(); 177 | _data.recycle(); 178 | } 179 | } 180 | } 181 | static final int TRANSACTION_connect = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); 182 | static final int TRANSACTION_disconnect = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); 183 | static final int TRANSACTION_checkStatus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); 184 | } 185 | /** 186 | * Sets up the VPN connection. 187 | * @param profile the profile object 188 | * @param username the username for authentication 189 | * @param password the corresponding password for authentication 190 | */ 191 | public boolean connect(android.net.vpn.VpnProfile profile, java.lang.String username, java.lang.String password) throws android.os.RemoteException; 192 | /** 193 | * Tears down the VPN connection. 194 | */ 195 | public void disconnect() throws android.os.RemoteException; 196 | /** 197 | * Makes the service broadcast the connectivity state. 198 | */ 199 | public void checkStatus(android.net.vpn.VpnProfile profile) throws android.os.RemoteException; 200 | } 201 | -------------------------------------------------------------------------------- /gen/com/android/server/vpn/R.java: -------------------------------------------------------------------------------- 1 | /* AUTO-GENERATED FILE. DO NOT MODIFY. 2 | * 3 | * This class was automatically generated by the 4 | * aapt tool from the resource data it found. It 5 | * should not be modified by hand. 6 | */ 7 | 8 | package com.android.server.vpn; 9 | 10 | public final class R { 11 | public static final class attr { 12 | } 13 | public static final class drawable { 14 | public static final int vpn_connected=0x7f020000; 15 | public static final int vpn_disconnected=0x7f020001; 16 | } 17 | public static final class layout { 18 | public static final int main=0x7f030000; 19 | } 20 | public static final class string { 21 | /** Title for the VPN Services activity. 22 | Title for the VPN Services activity. 23 | Title for the VPN Services activity. 24 | */ 25 | public static final int app_label=0x7f040000; 26 | public static final int app_name=0x7f040001; 27 | public static final int l2tp_ipsec_crt_vpn_description=0x7f040008; 28 | public static final int l2tp_ipsec_psk_vpn_description=0x7f040007; 29 | public static final int l2tp_vpn_description=0x7f040006; 30 | public static final int pptp_vpn_description=0x7f040005; 31 | public static final int vpn_notification_hint_disconnected=0x7f040004; 32 | public static final int vpn_notification_title_connected=0x7f040002; 33 | public static final int vpn_notification_title_disconnected=0x7f040003; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /proguard.cfg: -------------------------------------------------------------------------------- 1 | -optimizationpasses 5 2 | -dontusemixedcaseclassnames 3 | -dontskipnonpubliclibraryclasses 4 | -dontpreverify 5 | -verbose 6 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 7 | 8 | -keep public class * extends android.app.Activity 9 | -keep public class * extends android.app.Application 10 | -keep public class * extends android.app.Service 11 | -keep public class * extends android.content.BroadcastReceiver 12 | -keep public class * extends android.content.ContentProvider 13 | -keep public class * extends android.app.backup.BackupAgentHelper 14 | -keep public class * extends android.preference.Preference 15 | -keep public class com.android.vending.licensing.ILicensingService 16 | 17 | -keepclasseswithmembernames class * { 18 | native ; 19 | } 20 | 21 | -keepclasseswithmembernames class * { 22 | public (android.content.Context, android.util.AttributeSet); 23 | } 24 | 25 | -keepclasseswithmembernames class * { 26 | public (android.content.Context, android.util.AttributeSet, int); 27 | } 28 | 29 | -keepclassmembers enum * { 30 | public static **[] values(); 31 | public static ** valueOf(java.lang.String); 32 | } 33 | 34 | -keep class * implements android.os.Parcelable { 35 | public static final android.os.Parcelable$Creator *; 36 | } 37 | -------------------------------------------------------------------------------- /res.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/res.tar -------------------------------------------------------------------------------- /res/drawable/vpn_connected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/res/drawable/vpn_connected.png -------------------------------------------------------------------------------- /res/drawable/vpn_disconnected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luojiesi/android-vpn-server/47abc36c075a58005a94be5970d002c96fdb5b85/res/drawable/vpn_disconnected.png -------------------------------------------------------------------------------- /res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /res/values-ar/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "خدمات الشبكة الظاهرية الخاصة (VPN)" 5 | "تم توصيل الشبكة الظاهرية الخاصة (VPN) لـ %s" 6 | "تم فصل الشبكة الظاهرية الخاصة (VPN) لـ %s" 7 | "يمكنك اللمس لإعادة الاتصال بالشبكة الظاهرية الخاصة (VPN)." 8 | 9 | -------------------------------------------------------------------------------- /res/values-bg/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPN услуги" 5 | "Връзката с VPN %s бе установена" 6 | "Връзката с VPN %s бе прекъсната" 7 | "Докоснете за повторно свързване с VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-ca/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Serveis VPN" 5 | "VPN %s connectada" 6 | "VPN %s desconnectada" 7 | "Toqueu-ho per tornar-vos a connectar a una VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-cs/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Služby VPN" 5 | "Síť VPN %s je připojena" 6 | "Síť VPN %s odpojena" 7 | "Dotykem se znovu připojíte k síti VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-da/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPN-tjenester" 5 | "%s VPN forbundet" 6 | "%s VPN afbrudt" 7 | "Tryk for at oprette forbindelse til et VPN igen." 8 | 9 | -------------------------------------------------------------------------------- /res/values-de/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPN-Dienste" 5 | "%s mit VPN verbunden" 6 | "%s von VPN getrennt" 7 | "Zur Wiederherstellung der Verbindung mit einem VPN berühren" 8 | 9 | -------------------------------------------------------------------------------- /res/values-el/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Υπηρεσίες VPN" 5 | "Το VPN %s συνδέθηκε" 6 | "Το VPN %s αποσυνδέθηκε" 7 | "Πατήστε για να επανασυνδεθείτε σε ένα VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-en-rGB/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPN Services" 5 | "%s VPN connected" 6 | "%s VPN disconnected" 7 | "Touch to reconnect to a VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-es-rUS/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | VPN Services 5 | VPN Services 6 | 7 | %s VPN connected 8 | %s VPN disconnected 9 | Touch to reconnect to a VPN. 10 | pptp_vpn_description 11 | l2tp_vpn_description 12 | l2tp_ipsec_psk_vpn_description 13 | l2tp_ipsec_crt_vpn_description 14 | 15 | -------------------------------------------------------------------------------- /res/values-es/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Servicios VPN" 5 | "VPN %s conectada" 6 | "VPN %s desconectada" 7 | "Toca para volver a conectarte a una red VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-fa/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "سرویس های VPN" 5 | "%s VPN وصل شد" 6 | "%s VPN قطع شد" 7 | "برای اتصال مجدد به VPN لمس کنید." 8 | 9 | -------------------------------------------------------------------------------- /res/values-fi/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPN-palvelut" 5 | "%s: VPN-yhteys muodostettu" 6 | "%s: VPN-yhteys katkaistu" 7 | "Yhdistä VPN-verkkoon uudelleen koskettamalla." 8 | 9 | -------------------------------------------------------------------------------- /res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Services VPN" 5 | "VPN %s connecté" 6 | "VPN %s déconnecté" 7 | "Touchez l\'écran pour vous reconnecter à un VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-hr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPN usluge" 5 | "%s VPN priključen" 6 | "%s VPN je isključen" 7 | "Dotaknite za ponovno povezivanje s VPN-om." 8 | 9 | -------------------------------------------------------------------------------- /res/values-hu/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPN-szolgáltatások" 5 | "Kapcsolódva a(z) %s virtuális magánhálózathoz" 6 | "Kapcsolat bontva a(z) %s virtuális magánhálózattal" 7 | "Érintse meg az újracsatlakozáshoz." 8 | 9 | -------------------------------------------------------------------------------- /res/values-in/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Layanan VPN" 5 | "VPN %s terhubung" 6 | "VPN %s terputus" 7 | "Sentuh untuk terhubung kembali ke suatu VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-it/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Servizi VPN" 5 | "VPN %s collegata" 6 | "VPN %s scollegata" 7 | "Tocca per riconnetterti a una rete VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-iw/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "שירותי VPN" 5 | "VPN של %s מחובר" 6 | "VPN של %s נותק" 7 | "גע כדי להתחבר שוב ל-VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-ja/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPNサービス" 5 | "%s VPNが接続されました" 6 | "%s VPNが切断されました" 7 | "タップしてVPNに再接続してください。" 8 | 9 | -------------------------------------------------------------------------------- /res/values-ko/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPN 서비스" 5 | "%s VPN 연결됨" 6 | "%s VPN 연결 끊김" 7 | "VPN에 다시 연결하려면 터치하세요." 8 | 9 | -------------------------------------------------------------------------------- /res/values-lt/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPT paslaugos" 5 | "%s VPT prijungtas" 6 | "%s VPT atjungtas" 7 | "Palieskite, kad būtų iš naujo sujungta su VPT." 8 | 9 | -------------------------------------------------------------------------------- /res/values-lv/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPN pakalpojumi" 5 | "%s VPN ir savienots" 6 | "%s VPN ir atvienots" 7 | "Pieskarieties, lai atkārtoti izveidotu savienojumu ar VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-nb/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPN-tjenester" 5 | "Koblet til VPNet %s" 6 | "Koblet fra VPNet %s" 7 | "Trykk for å koble til et VPN på nytt" 8 | 9 | -------------------------------------------------------------------------------- /res/values-nl/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPN-services" 5 | "%s verbonden via VPN" 6 | "VPN-verbinding met %s verbroken" 7 | "Raak aan om opnieuw verbinding te maken met een VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-pl/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Usługi VPN" 5 | "Połączono z siecią VPN %s" 6 | "Rozłączono z siecią VPN %s" 7 | "Dotknij, aby ponownie połączyć się z siecią VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-pt-rPT/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Serviços VPN" 5 | "VPN %s ligado" 6 | "VPN %s desligado" 7 | "Toque para voltar a ligar a uma VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-pt/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Serviços de VPN" 5 | "VPN de %s conectada" 6 | "VPN de %s desconectada" 7 | "Toque para reconectar-se a uma VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-rm/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Servetschs VPN" 5 | "VPN %s connectà" 6 | "VPN %s deconnectà" 7 | "Tutgar per reconnectar ad in VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-ro/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Servicii VPN" 5 | "VPN %s conectat" 6 | "VPN %s deconectat" 7 | "Atingeţi pentru a vă reconecta la o reţea VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-ru/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Службы VPN" 5 | "Сеть VPN (%s) подключена" 6 | "Сеть VPN (%s) отключена" 7 | "Нажмите, чтобы повторно подключиться к VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-sk/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Služby VPN" 5 | "Sieť VPN %s je pripojená" 6 | "Sieť VPN %s odpojená" 7 | "Dotykom sa znova pripojíte k sieti VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-sl/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Storitve VPN" 5 | "VPN profila %s je povezan" 6 | "VPN profila %s je izklopljen" 7 | "Dotaknite se, če želite preklopiti v navidezno zasebno omrežje." 8 | 9 | -------------------------------------------------------------------------------- /res/values-sr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPN услуге" 5 | "%s VPN веза је успостављена" 6 | "%s VPN веза је прекинута" 7 | "Додирните да бисте се поново повезали са VPN-ом." 8 | 9 | -------------------------------------------------------------------------------- /res/values-sv/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPN-tjänster" 5 | "%s VPN anslutet" 6 | "%s VPN frånkopplat" 7 | "Tryck här om du vill återansluta till ett VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-th/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "บริการ VPN" 5 | "%s เชื่อมต่อ VPN แล้ว" 6 | "%s ตัดการเชื่อมต่อ VPN แล้ว" 7 | "แตะเพื่อเชื่อมต่อกับ VPN อีกครั้ง" 8 | 9 | -------------------------------------------------------------------------------- /res/values-tl/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Mga serbisyo ng VPN" 5 | "Hindi konektado ang %s VPN" 6 | "Hindi konektado ang %s VPN" 7 | "Galawin upang muling kumonekta sa VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-tr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPN Hizmetleri" 5 | "%s VPN bağlandı" 6 | "%s VPN bağlantısı kesildi" 7 | "Bir VPN\'ye tekrar bağlanmak için dokunun." 8 | 9 | -------------------------------------------------------------------------------- /res/values-uk/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Служби VPN" 5 | "%s підключ. ч-з VPN" 6 | "VPN %s роз\'єднано" 7 | "Натисн. для повт. з\'єдн. з VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-vi/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "Dịch vụ VPN" 5 | "Đã kết nối VPN %s" 6 | "Đã ngắt kết nối VPN %s" 7 | "Chạm để kết nối lại với VPN." 8 | 9 | -------------------------------------------------------------------------------- /res/values-zh-rCN/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | VPN Services 5 | VPN Services 6 | 7 | %s VPN connected 8 | %s VPN disconnected 9 | Touch to reconnect to a VPN. 10 | pptp_vpn_description 11 | l2tp_vpn_description 12 | l2tp_ipsec_psk_vpn_description 13 | l2tp_ipsec_crt_vpn_description 14 | 15 | -------------------------------------------------------------------------------- /res/values-zh-rTW/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | "VPN 服務" 5 | "%s VPN 已連線" 6 | "%s VPN 已中斷連線" 7 | "輕觸即可重新連線至 VPN。" 8 | 9 | -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | VPN Services 5 | android-vpn-server 6 | 7 | %s VPN connected 8 | %s VPN disconnected 9 | Touch to reconnect to a VPN. 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/android/net/vpn/IVpnService.aidl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.net.vpn; 18 | 19 | import android.net.vpn.VpnProfile; 20 | 21 | /** 22 | * Interface to access a VPN service. 23 | * {@hide} 24 | */ 25 | interface IVpnService { 26 | /** 27 | * Sets up the VPN connection. 28 | * @param profile the profile object 29 | * @param username the username for authentication 30 | * @param password the corresponding password for authentication 31 | */ 32 | boolean connect(in VpnProfile profile, String username, String password); 33 | 34 | /** 35 | * Tears down the VPN connection. 36 | */ 37 | void disconnect(); 38 | 39 | /** 40 | * Makes the service broadcast the connectivity state. 41 | */ 42 | void checkStatus(in VpnProfile profile); 43 | } 44 | -------------------------------------------------------------------------------- /src/android/net/vpn/L2tpIpsecProfile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.net.vpn; 18 | 19 | import android.os.Parcel; 20 | 21 | /** 22 | * The profile for certificate-based L2TP-over-IPSec type of VPN. 23 | * {@hide} 24 | */ 25 | public class L2tpIpsecProfile extends L2tpProfile { 26 | private static final long serialVersionUID = 1L; 27 | 28 | private String mUserCertificate; 29 | private String mCaCertificate; 30 | 31 | @Override 32 | public VpnType getType() { 33 | return VpnType.L2TP_IPSEC; 34 | } 35 | 36 | public void setCaCertificate(String name) { 37 | mCaCertificate = name; 38 | } 39 | 40 | public String getCaCertificate() { 41 | return mCaCertificate; 42 | } 43 | 44 | public void setUserCertificate(String name) { 45 | mUserCertificate = name; 46 | } 47 | 48 | public String getUserCertificate() { 49 | return mUserCertificate; 50 | } 51 | 52 | @Override 53 | protected void readFromParcel(Parcel in) { 54 | super.readFromParcel(in); 55 | mCaCertificate = in.readString(); 56 | mUserCertificate = in.readString(); 57 | } 58 | 59 | @Override 60 | public void writeToParcel(Parcel parcel, int flags) { 61 | super.writeToParcel(parcel, flags); 62 | parcel.writeString(mCaCertificate); 63 | parcel.writeString(mUserCertificate); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/android/net/vpn/L2tpIpsecPskProfile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.net.vpn; 18 | 19 | import android.os.Parcel; 20 | 21 | /** 22 | * The profile for pre-shared-key-based L2TP-over-IPSec type of VPN. 23 | * {@hide} 24 | */ 25 | public class L2tpIpsecPskProfile extends L2tpProfile { 26 | private static final long serialVersionUID = 1L; 27 | 28 | private String mPresharedKey; 29 | 30 | @Override 31 | public VpnType getType() { 32 | return VpnType.L2TP_IPSEC_PSK; 33 | } 34 | 35 | public void setPresharedKey(String key) { 36 | mPresharedKey = key; 37 | } 38 | 39 | public String getPresharedKey() { 40 | return mPresharedKey; 41 | } 42 | 43 | @Override 44 | protected void readFromParcel(Parcel in) { 45 | super.readFromParcel(in); 46 | mPresharedKey = in.readString(); 47 | } 48 | 49 | @Override 50 | public void writeToParcel(Parcel parcel, int flags) { 51 | super.writeToParcel(parcel, flags); 52 | parcel.writeString(mPresharedKey); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/android/net/vpn/L2tpProfile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.net.vpn; 18 | 19 | import android.os.Parcel; 20 | 21 | /** 22 | * The profile for L2TP type of VPN. 23 | * {@hide} 24 | */ 25 | public class L2tpProfile extends VpnProfile { 26 | private static final long serialVersionUID = 1L; 27 | 28 | private boolean mSecret; 29 | private String mSecretString; 30 | 31 | @Override 32 | public VpnType getType() { 33 | return VpnType.L2TP; 34 | } 35 | 36 | /** 37 | * Enables/disables the secret for authenticating tunnel connection. 38 | */ 39 | public void setSecretEnabled(boolean enabled) { 40 | mSecret = enabled; 41 | } 42 | 43 | public boolean isSecretEnabled() { 44 | return mSecret; 45 | } 46 | 47 | public void setSecretString(String secret) { 48 | mSecretString = secret; 49 | } 50 | 51 | public String getSecretString() { 52 | return mSecretString; 53 | } 54 | 55 | @Override 56 | protected void readFromParcel(Parcel in) { 57 | super.readFromParcel(in); 58 | mSecret = in.readInt() > 0; 59 | mSecretString = in.readString(); 60 | } 61 | 62 | @Override 63 | public void writeToParcel(Parcel parcel, int flags) { 64 | super.writeToParcel(parcel, flags); 65 | parcel.writeInt(mSecret ? 1 : 0); 66 | parcel.writeString(mSecretString); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/android/net/vpn/PptpProfile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.net.vpn; 18 | 19 | import android.os.Parcel; 20 | 21 | /** 22 | * The profile for PPTP type of VPN. 23 | * {@hide} 24 | */ 25 | public class PptpProfile extends VpnProfile { 26 | private static final long serialVersionUID = 1L; 27 | private boolean mEncryption = true; 28 | 29 | @Override 30 | public VpnType getType() { 31 | return VpnType.PPTP; 32 | } 33 | 34 | /** 35 | * Enables/disables the encryption for PPTP tunnel. 36 | */ 37 | public void setEncryptionEnabled(boolean enabled) { 38 | mEncryption = enabled; 39 | } 40 | 41 | public boolean isEncryptionEnabled() { 42 | return mEncryption; 43 | } 44 | 45 | @Override 46 | protected void readFromParcel(Parcel in) { 47 | super.readFromParcel(in); 48 | mEncryption = in.readInt() > 0; 49 | } 50 | 51 | @Override 52 | public void writeToParcel(Parcel parcel, int flags) { 53 | super.writeToParcel(parcel, flags); 54 | parcel.writeInt(mEncryption ? 1 : 0); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/android/net/vpn/VpnManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.net.vpn; 18 | 19 | import java.io.File; 20 | 21 | import android.content.BroadcastReceiver; 22 | import android.content.Context; 23 | import android.content.Intent; 24 | import android.content.IntentFilter; 25 | import android.content.ServiceConnection; 26 | import android.os.Environment; 27 | import android.os.SystemProperties; 28 | import android.util.Log; 29 | 30 | /** 31 | * The class provides interface to manage all VPN-related tasks, including: 32 | *
    33 | *
  • The list of supported VPN types. 34 | *
  • API's to start/stop the service of a particular type. 35 | *
  • API's to start the settings activity. 36 | *
  • API's to create a profile. 37 | *
  • API's to register/unregister a connectivity receiver and the keys to 38 | * access the fields in a connectivity broadcast event. 39 | *
40 | * {@hide} 41 | */ 42 | public class VpnManager { 43 | // Action for broadcasting a connectivity state. 44 | private static final String ACTION_VPN_CONNECTIVITY = "vpn.connectivity"; 45 | /** Key to the profile name of a connectivity broadcast event. */ 46 | public static final String BROADCAST_PROFILE_NAME = "profile_name"; 47 | /** Key to the connectivity state of a connectivity broadcast event. */ 48 | public static final String BROADCAST_CONNECTION_STATE = "connection_state"; 49 | /** Key to the error code of a connectivity broadcast event. */ 50 | public static final String BROADCAST_ERROR_CODE = "err"; 51 | /** Error code to indicate an error from authentication. */ 52 | public static final int VPN_ERROR_AUTH = 51; 53 | /** Error code to indicate the connection attempt failed. */ 54 | public static final int VPN_ERROR_CONNECTION_FAILED = 101; 55 | /** Error code to indicate the server is not known. */ 56 | public static final int VPN_ERROR_UNKNOWN_SERVER = 102; 57 | /** Error code to indicate an error from challenge response. */ 58 | public static final int VPN_ERROR_CHALLENGE = 5; 59 | /** Error code to indicate an error of remote server hanging up. */ 60 | public static final int VPN_ERROR_REMOTE_HUNG_UP = 7; 61 | /** Error code to indicate an error of remote PPP server hanging up. */ 62 | public static final int VPN_ERROR_REMOTE_PPP_HUNG_UP = 48; 63 | /** Error code to indicate a PPP negotiation error. */ 64 | public static final int VPN_ERROR_PPP_NEGOTIATION_FAILED = 42; 65 | /** Error code to indicate an error of losing connectivity. */ 66 | public static final int VPN_ERROR_CONNECTION_LOST = 103; 67 | /** Largest error code used by VPN. */ 68 | public static final int VPN_ERROR_LARGEST = 200; 69 | /** Error code to indicate a successful connection. */ 70 | public static final int VPN_ERROR_NO_ERROR = 0; 71 | 72 | public static final String PROFILES_PATH = "/misc/vpn/profiles"; 73 | 74 | private static final String PACKAGE_PREFIX = 75 | VpnManager.class.getPackage().getName() + "."; 76 | 77 | // Action to start VPN service 78 | private static final String ACTION_VPN_SERVICE = PACKAGE_PREFIX + "SERVICE"; 79 | 80 | // Action to start VPN settings 81 | private static final String ACTION_VPN_SETTINGS = 82 | PACKAGE_PREFIX + "SETTINGS"; 83 | 84 | public static final String TAG = VpnManager.class.getSimpleName(); 85 | 86 | // TODO(oam): Test VPN when EFS is enabled (will do later)... 87 | public static String getProfilePath() { 88 | return Environment.getDataDirectory().getPath() + PROFILES_PATH; 89 | } 90 | 91 | /** 92 | * Returns all supported VPN types. 93 | */ 94 | public static VpnType[] getSupportedVpnTypes() { 95 | return VpnType.values(); 96 | } 97 | 98 | private Context mContext; 99 | 100 | /** 101 | * Creates a manager object with the specified context. 102 | */ 103 | public VpnManager(Context c) { 104 | mContext = c; 105 | } 106 | 107 | /** 108 | * Creates a VPN profile of the specified type. 109 | * 110 | * @param type the VPN type 111 | * @return the profile object 112 | */ 113 | public VpnProfile createVpnProfile(VpnType type) { 114 | return createVpnProfile(type, false); 115 | } 116 | 117 | /** 118 | * Creates a VPN profile of the specified type. 119 | * 120 | * @param type the VPN type 121 | * @param customized true if the profile is custom made 122 | * @return the profile object 123 | */ 124 | public VpnProfile createVpnProfile(VpnType type, boolean customized) { 125 | try { 126 | VpnProfile p = (VpnProfile) type.getProfileClass().newInstance(); 127 | p.setCustomized(customized); 128 | return p; 129 | } catch (InstantiationException e) { 130 | return null; 131 | } catch (IllegalAccessException e) { 132 | return null; 133 | } 134 | } 135 | 136 | /** 137 | * Starts the VPN service to establish VPN connection. 138 | */ 139 | public void startVpnService() { 140 | mContext.startService(new Intent(ACTION_VPN_SERVICE)); 141 | } 142 | 143 | /** 144 | * Stops the VPN service. 145 | */ 146 | public void stopVpnService() { 147 | mContext.stopService(new Intent(ACTION_VPN_SERVICE)); 148 | } 149 | 150 | /** 151 | * Binds the specified ServiceConnection with the VPN service. 152 | */ 153 | public boolean bindVpnService(ServiceConnection c) { 154 | if (!mContext.bindService(new Intent(ACTION_VPN_SERVICE), c, 0)) { 155 | Log.w(TAG, "failed to connect to VPN service"); 156 | return false; 157 | } else { 158 | Log.d(TAG, "succeeded to connect to VPN service"); 159 | return true; 160 | } 161 | } 162 | 163 | /** Broadcasts the connectivity state of the specified profile. */ 164 | public void broadcastConnectivity(String profileName, VpnState s) { 165 | broadcastConnectivity(profileName, s, VPN_ERROR_NO_ERROR); 166 | } 167 | 168 | /** Broadcasts the connectivity state with an error code. */ 169 | public void broadcastConnectivity(String profileName, VpnState s, 170 | int error) { 171 | Intent intent = new Intent(ACTION_VPN_CONNECTIVITY); 172 | intent.putExtra(BROADCAST_PROFILE_NAME, profileName); 173 | intent.putExtra(BROADCAST_CONNECTION_STATE, s); 174 | if (error != VPN_ERROR_NO_ERROR) { 175 | intent.putExtra(BROADCAST_ERROR_CODE, error); 176 | } 177 | mContext.sendBroadcast(intent); 178 | } 179 | 180 | public void registerConnectivityReceiver(BroadcastReceiver r) { 181 | IntentFilter filter = new IntentFilter(); 182 | filter.addAction(VpnManager.ACTION_VPN_CONNECTIVITY); 183 | mContext.registerReceiver(r, filter); 184 | } 185 | 186 | public void unregisterConnectivityReceiver(BroadcastReceiver r) { 187 | mContext.unregisterReceiver(r); 188 | } 189 | 190 | /** Starts the VPN settings activity. */ 191 | public void startSettingsActivity() { 192 | Intent intent = new Intent(ACTION_VPN_SETTINGS); 193 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 194 | mContext.startActivity(intent); 195 | } 196 | 197 | /** Creates an intent to start the VPN settings activity. */ 198 | public Intent createSettingsActivityIntent() { 199 | Intent intent = new Intent(ACTION_VPN_SETTINGS); 200 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 201 | return intent; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/android/net/vpn/VpnProfile.aidl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.net.vpn; 18 | 19 | parcelable VpnProfile; 20 | -------------------------------------------------------------------------------- /src/android/net/vpn/VpnProfile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.net.vpn; 18 | 19 | import android.content.Context; 20 | import android.os.Parcel; 21 | import android.os.Parcelable; 22 | 23 | import java.io.IOException; 24 | import java.io.Serializable; 25 | 26 | /** 27 | * A VPN profile. 28 | * {@hide} 29 | */ 30 | public abstract class VpnProfile implements Parcelable, Serializable { 31 | private static final long serialVersionUID = 1L; 32 | private String mName; // unique display name 33 | private String mId; // unique identifier 34 | private String mServerName; // VPN server name 35 | private String mDomainSuffices; // space separated list 36 | private String mRouteList; // space separated list 37 | private String mSavedUsername; 38 | private boolean mIsCustomized; 39 | private transient VpnState mState = VpnState.IDLE; 40 | 41 | /** Sets a user-friendly name for this profile. */ 42 | public void setName(String name) { 43 | mName = name; 44 | } 45 | 46 | public String getName() { 47 | return mName; 48 | } 49 | 50 | /** 51 | * Sets an ID for this profile. The caller should make sure the 52 | * uniqueness of the ID. 53 | */ 54 | public void setId(String id) { 55 | mId = id; 56 | } 57 | 58 | public String getId() { 59 | return mId; 60 | } 61 | 62 | /** 63 | * Sets the name of the VPN server. Used for DNS lookup. 64 | */ 65 | public void setServerName(String name) { 66 | mServerName = name; 67 | } 68 | 69 | public String getServerName() { 70 | return mServerName; 71 | } 72 | 73 | /** 74 | * Sets the domain suffices for DNS resolution. 75 | * 76 | * @param entries a comma-separated list of domain suffices 77 | */ 78 | public void setDomainSuffices(String entries) { 79 | mDomainSuffices = entries; 80 | } 81 | 82 | public String getDomainSuffices() { 83 | return mDomainSuffices; 84 | } 85 | 86 | /** 87 | * Sets the routing info for this VPN connection. 88 | * 89 | * @param entries a comma-separated list of routes; each entry is in the 90 | * format of "(network address)/(network mask)" 91 | */ 92 | public void setRouteList(String entries) { 93 | mRouteList = entries; 94 | } 95 | 96 | public String getRouteList() { 97 | return mRouteList; 98 | } 99 | 100 | public void setSavedUsername(String name) { 101 | mSavedUsername = name; 102 | } 103 | 104 | public String getSavedUsername() { 105 | return mSavedUsername; 106 | } 107 | 108 | public void setState(VpnState state) { 109 | mState = state; 110 | } 111 | 112 | public VpnState getState() { 113 | return ((mState == null) ? VpnState.IDLE : mState); 114 | } 115 | 116 | public boolean isIdle() { 117 | return (mState == VpnState.IDLE); 118 | } 119 | 120 | /** 121 | * Returns whether this profile is custom made (as opposed to being 122 | * created by provided user interface). 123 | */ 124 | public boolean isCustomized() { 125 | return mIsCustomized; 126 | } 127 | 128 | /** 129 | * Returns the VPN type of the profile. 130 | */ 131 | public abstract VpnType getType(); 132 | 133 | void setCustomized(boolean customized) { 134 | mIsCustomized = customized; 135 | } 136 | 137 | protected void readFromParcel(Parcel in) { 138 | mName = in.readString(); 139 | mId = in.readString(); 140 | mServerName = in.readString(); 141 | mDomainSuffices = in.readString(); 142 | mRouteList = in.readString(); 143 | mSavedUsername = in.readString(); 144 | } 145 | 146 | public static final Parcelable.Creator CREATOR = 147 | new Parcelable.Creator() { 148 | public VpnProfile createFromParcel(Parcel in) { 149 | VpnType type = Enum.valueOf(VpnType.class, in.readString()); 150 | boolean customized = in.readInt() > 0; 151 | VpnProfile p = new VpnManager(null).createVpnProfile(type, 152 | customized); 153 | if (p == null) return null; 154 | p.readFromParcel(in); 155 | return p; 156 | } 157 | 158 | public VpnProfile[] newArray(int size) { 159 | return new VpnProfile[size]; 160 | } 161 | }; 162 | 163 | public void writeToParcel(Parcel parcel, int flags) { 164 | parcel.writeString(getType().toString()); 165 | parcel.writeInt(mIsCustomized ? 1 : 0); 166 | parcel.writeString(mName); 167 | parcel.writeString(mId); 168 | parcel.writeString(mServerName); 169 | parcel.writeString(mDomainSuffices); 170 | parcel.writeString(mRouteList); 171 | parcel.writeString(mSavedUsername); 172 | } 173 | 174 | public int describeContents() { 175 | return 0; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/android/net/vpn/VpnState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.net.vpn; 18 | 19 | /** 20 | * Enumeration of all VPN states. 21 | * 22 | * A normal VPN connection lifetime starts in {@link IDLE}. When a new 23 | * connection is about to be set up, it goes to {@link CONNECTING} and then 24 | * {@link CONNECTED} if successful; back to {@link IDLE} if failed. 25 | * When the connection is about to be torn down, it goes to 26 | * {@link DISCONNECTING} and then {@link IDLE}. 27 | * {@link CANCELLED} is a state when a VPN connection attempt is aborted, and 28 | * is in transition to {@link IDLE}. 29 | * The {@link UNUSABLE} state indicates that the profile is not in a state for 30 | * connecting due to possibly the integrity of the fields or another profile is 31 | * connecting etc. 32 | * The {@link UNKNOWN} state indicates that the profile state is to be 33 | * determined. 34 | * {@hide} 35 | */ 36 | public enum VpnState { 37 | CONNECTING, DISCONNECTING, CANCELLED, CONNECTED, IDLE, UNUSABLE, UNKNOWN 38 | } 39 | -------------------------------------------------------------------------------- /src/android/net/vpn/VpnTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.net.vpn; 18 | 19 | import android.content.BroadcastReceiver; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.net.vpn.L2tpProfile; 23 | import android.net.vpn.L2tpIpsecProfile; 24 | import android.net.vpn.L2tpIpsecPskProfile; 25 | import android.net.vpn.PptpProfile; 26 | import android.net.vpn.VpnManager; 27 | import android.net.vpn.VpnProfile; 28 | import android.net.vpn.VpnState; 29 | import android.net.vpn.VpnType; 30 | import android.os.ConditionVariable; 31 | import android.os.Parcel; 32 | import android.test.AndroidTestCase; 33 | import android.test.suitebuilder.annotation.SmallTest; 34 | import android.text.TextUtils; 35 | 36 | /** 37 | * Unit test class to test VPN api 38 | * Use the below command to run the vpn unit test only 39 | * runtest vpntest or 40 | * adb shell am instrument -e class 'com.android.unit_tests.VpnTest' 41 | * -w com.android.unit_tests/android.test.InstrumentationTestRunner 42 | */ 43 | public class VpnTest extends AndroidTestCase { 44 | private static final String NAME = "a name"; 45 | private static final String SERVER_NAME = "a server name"; 46 | private static final String ID = "some id"; 47 | private static final String SUFFICES = "some suffices"; 48 | private static final String ROUTES = "some routes"; 49 | private static final String SAVED_NAME = "some name"; 50 | 51 | @Override 52 | public void setUp() { 53 | } 54 | 55 | @Override 56 | public void tearDown() { 57 | } 58 | 59 | @SmallTest 60 | public void testVpnType() { 61 | testVpnType(VpnType.L2TP); 62 | testVpnType(VpnType.L2TP_IPSEC); 63 | testVpnType(VpnType.L2TP_IPSEC_PSK); 64 | testVpnType(VpnType.PPTP); 65 | } 66 | 67 | @SmallTest 68 | public void testVpnProfile() { 69 | VpnState state = VpnState.CONNECTING; 70 | testVpnProfile(createTestProfile(state), state); 71 | } 72 | 73 | @SmallTest 74 | public void testGetType() { 75 | assertEquals(VpnType.L2TP, new L2tpProfile().getType()); 76 | assertEquals(VpnType.L2TP_IPSEC, new L2tpIpsecProfile().getType()); 77 | assertEquals(VpnType.L2TP_IPSEC_PSK, 78 | new L2tpIpsecPskProfile().getType()); 79 | assertEquals(VpnType.PPTP, new PptpProfile().getType()); 80 | } 81 | 82 | @SmallTest 83 | public void testVpnTypes() { 84 | assertTrue(VpnManager.getSupportedVpnTypes().length > 0); 85 | } 86 | 87 | @SmallTest 88 | public void testGetTypeFromManager() { 89 | VpnManager m = new VpnManager(getContext()); 90 | VpnType[] types = VpnManager.getSupportedVpnTypes(); 91 | for (VpnType t : types) { 92 | assertEquals(t, m.createVpnProfile(t).getType()); 93 | } 94 | } 95 | 96 | @SmallTest 97 | public void testParcelable() { 98 | VpnProfile p = createTestProfile(VpnState.CONNECTED); 99 | Parcel parcel = Parcel.obtain(); 100 | p.writeToParcel(parcel, 0); 101 | parcel.setDataPosition(0); 102 | 103 | // VpnState is transient and not saved in the parcel 104 | testVpnProfile(VpnProfile.CREATOR.createFromParcel(parcel), null); 105 | } 106 | 107 | @SmallTest 108 | public void testReceiver() { 109 | final String profileName = "whatever"; 110 | final VpnState state = VpnState.DISCONNECTING; 111 | final ConditionVariable cv = new ConditionVariable(); 112 | cv.close(); 113 | BroadcastReceiver r = new BroadcastReceiver() { 114 | public void onReceive(Context c, Intent i) { 115 | assertEquals(profileName, 116 | i.getStringExtra(VpnManager.BROADCAST_PROFILE_NAME)); 117 | assertEquals(state, i.getSerializableExtra( 118 | VpnManager.BROADCAST_CONNECTION_STATE)); 119 | cv.open(); 120 | } 121 | }; 122 | 123 | VpnManager m = new VpnManager(getContext()); 124 | m.registerConnectivityReceiver(r); 125 | m.broadcastConnectivity(profileName, state); 126 | 127 | // fail it if onReceive() doesn't get executed in 5 sec 128 | assertTrue(cv.block(5000)); 129 | } 130 | 131 | private void testVpnType(VpnType type) { 132 | assertFalse(TextUtils.isEmpty(type.getDisplayName())); 133 | assertNotNull(type.getProfileClass()); 134 | } 135 | 136 | private VpnProfile createTestProfile(VpnState state) { 137 | VpnProfile p = new L2tpProfile(); 138 | p.setName(NAME); 139 | p.setServerName(SERVER_NAME); 140 | p.setId(ID); 141 | p.setDomainSuffices(SUFFICES); 142 | p.setRouteList(ROUTES); 143 | p.setSavedUsername(SAVED_NAME); 144 | p.setState(state); 145 | return p; 146 | } 147 | 148 | private void testVpnProfile(VpnProfile p, VpnState state) { 149 | assertEquals(NAME, p.getName()); 150 | assertEquals(SERVER_NAME, p.getServerName()); 151 | assertEquals(ID, p.getId()); 152 | assertEquals(SUFFICES, p.getDomainSuffices()); 153 | assertEquals(ROUTES, p.getRouteList()); 154 | assertEquals(SAVED_NAME, p.getSavedUsername()); 155 | if (state != null) assertEquals(state, p.getState()); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/android/net/vpn/VpnType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.net.vpn; 18 | 19 | import com.android.server.vpn.R; 20 | 21 | /** 22 | * Enumeration of all supported VPN types. 23 | * {@hide} 24 | */ 25 | public enum VpnType { 26 | 27 | PPTP("PPTP", R.string.pptp_vpn_description, PptpProfile.class), 28 | L2TP("L2TP", R.string.l2tp_vpn_description, L2tpProfile.class), 29 | L2TP_IPSEC_PSK("L2TP/IPSec PSK", R.string.l2tp_ipsec_psk_vpn_description, 30 | L2tpIpsecPskProfile.class), 31 | L2TP_IPSEC("L2TP/IPSec CRT", R.string.l2tp_ipsec_crt_vpn_description, 32 | L2tpIpsecProfile.class); 33 | 34 | VpnType(String displayName, int descriptionId, 35 | Class klass) { 36 | mDisplayName = displayName; 37 | mDescriptionId = descriptionId; 38 | mClass = klass; 39 | } 40 | 41 | private String mDisplayName; 42 | private int mDescriptionId; 43 | private Class mClass; 44 | 45 | public String getDisplayName() { 46 | return mDisplayName; 47 | } 48 | 49 | public int getDescriptionId() { 50 | return mDescriptionId; 51 | } 52 | 53 | public Class getProfileClass() { 54 | return mClass; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/android/os/SystemProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.os; 18 | 19 | 20 | /** 21 | * Gives access to the system properties store. The system properties 22 | * store contains a list of string key-value pairs. 23 | * 24 | * {@hide} 25 | */ 26 | public class SystemProperties 27 | { 28 | public static final int PROP_NAME_MAX = 31; 29 | public static final int PROP_VALUE_MAX = 91; 30 | 31 | private static native String native_get(String key); 32 | private static native String native_get(String key, String def); 33 | private static native int native_get_int(String key, int def); 34 | private static native long native_get_long(String key, long def); 35 | private static native boolean native_get_boolean(String key, boolean def); 36 | private static native void native_set(String key, String def); 37 | 38 | /** 39 | * Get the value for the given key. 40 | * @return an empty string if the key isn't found 41 | * @throws IllegalArgumentException if the key exceeds 32 characters 42 | */ 43 | public static String get(String key) { 44 | if (key.length() > PROP_NAME_MAX) { 45 | throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); 46 | } 47 | return native_get(key); 48 | } 49 | 50 | /** 51 | * Get the value for the given key. 52 | * @return if the key isn't found, return def if it isn't null, or an empty string otherwise 53 | * @throws IllegalArgumentException if the key exceeds 32 characters 54 | */ 55 | public static String get(String key, String def) { 56 | if (key.length() > PROP_NAME_MAX) { 57 | throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); 58 | } 59 | return native_get(key, def); 60 | } 61 | 62 | /** 63 | * Get the value for the given key, and return as an integer. 64 | * @param key the key to lookup 65 | * @param def a default value to return 66 | * @return the key parsed as an integer, or def if the key isn't found or 67 | * cannot be parsed 68 | * @throws IllegalArgumentException if the key exceeds 32 characters 69 | */ 70 | public static int getInt(String key, int def) { 71 | if (key.length() > PROP_NAME_MAX) { 72 | throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); 73 | } 74 | return native_get_int(key, def); 75 | } 76 | 77 | /** 78 | * Get the value for the given key, and return as a long. 79 | * @param key the key to lookup 80 | * @param def a default value to return 81 | * @return the key parsed as a long, or def if the key isn't found or 82 | * cannot be parsed 83 | * @throws IllegalArgumentException if the key exceeds 32 characters 84 | */ 85 | public static long getLong(String key, long def) { 86 | if (key.length() > PROP_NAME_MAX) { 87 | throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); 88 | } 89 | return native_get_long(key, def); 90 | } 91 | 92 | /** 93 | * Get the value for the given key, returned as a boolean. 94 | * Values 'n', 'no', '0', 'false' or 'off' are considered false. 95 | * Values 'y', 'yes', '1', 'true' or 'on' are considered true. 96 | * (case insensitive). 97 | * If the key does not exist, or has any other value, then the default 98 | * result is returned. 99 | * @param key the key to lookup 100 | * @param def a default value to return 101 | * @return the key parsed as a boolean, or def if the key isn't found or is 102 | * not able to be parsed as a boolean. 103 | * @throws IllegalArgumentException if the key exceeds 32 characters 104 | */ 105 | public static boolean getBoolean(String key, boolean def) { 106 | if (key.length() > PROP_NAME_MAX) { 107 | throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); 108 | } 109 | return native_get_boolean(key, def); 110 | } 111 | 112 | /** 113 | * Set the value for the given key. 114 | * @throws IllegalArgumentException if the key exceeds 32 characters 115 | * @throws IllegalArgumentException if the value exceeds 92 characters 116 | */ 117 | public static void set(String key, String val) { 118 | if (key.length() > PROP_NAME_MAX) { 119 | throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); 120 | } 121 | if (val != null && val.length() > PROP_VALUE_MAX) { 122 | throw new IllegalArgumentException("val.length > " + 123 | PROP_VALUE_MAX); 124 | } 125 | native_set(key, val); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/android/security/Credentials.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.security; 18 | 19 | import android.content.ActivityNotFoundException; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.util.Log; 23 | 24 | import java.security.KeyPair; 25 | 26 | /** 27 | * {@hide} 28 | */ 29 | public class Credentials { 30 | private static final String LOGTAG = "Credentials"; 31 | 32 | public static final String UNLOCK_ACTION = "android.credentials.UNLOCK"; 33 | 34 | public static final String INSTALL_ACTION = "android.credentials.INSTALL"; 35 | 36 | public static final String SYSTEM_INSTALL_ACTION = "android.credentials.SYSTEM_INSTALL"; 37 | 38 | /** Key prefix for CA certificates. */ 39 | public static final String CA_CERTIFICATE = "CACERT_"; 40 | 41 | /** Key prefix for user certificates. */ 42 | public static final String USER_CERTIFICATE = "USRCERT_"; 43 | 44 | /** Key prefix for user private keys. */ 45 | public static final String USER_PRIVATE_KEY = "USRPKEY_"; 46 | 47 | /** Key prefix for VPN. */ 48 | public static final String VPN = "VPN_"; 49 | 50 | /** Key prefix for WIFI. */ 51 | public static final String WIFI = "WIFI_"; 52 | 53 | /** Data type for public keys. */ 54 | public static final String PUBLIC_KEY = "KEY"; 55 | 56 | /** Data type for private keys. */ 57 | public static final String PRIVATE_KEY = "PKEY"; 58 | 59 | /** Data type for certificates. */ 60 | public static final String CERTIFICATE = "CERT"; 61 | 62 | /** Data type for PKCS12. */ 63 | public static final String PKCS12 = "PKCS12"; 64 | 65 | private static Credentials singleton; 66 | 67 | public static Credentials getInstance() { 68 | if (singleton == null) { 69 | singleton = new Credentials(); 70 | } 71 | return singleton; 72 | } 73 | 74 | public void unlock(Context context) { 75 | try { 76 | Intent intent = new Intent(UNLOCK_ACTION); 77 | context.startActivity(intent); 78 | } catch (ActivityNotFoundException e) { 79 | Log.w(LOGTAG, e.toString()); 80 | } 81 | } 82 | 83 | public void install(Context context, KeyPair pair) { 84 | try { 85 | Intent intent = new Intent(INSTALL_ACTION); 86 | intent.putExtra(PRIVATE_KEY, pair.getPrivate().getEncoded()); 87 | intent.putExtra(PUBLIC_KEY, pair.getPublic().getEncoded()); 88 | context.startActivity(intent); 89 | } catch (ActivityNotFoundException e) { 90 | Log.w(LOGTAG, e.toString()); 91 | } 92 | } 93 | 94 | public void install(Context context, String type, byte[] value) { 95 | try { 96 | Intent intent = new Intent(INSTALL_ACTION); 97 | intent.putExtra(type, value); 98 | context.startActivity(intent); 99 | } catch (ActivityNotFoundException e) { 100 | Log.w(LOGTAG, e.toString()); 101 | } 102 | } 103 | 104 | public void installFromSdCard(Context context) { 105 | try { 106 | context.startActivity(new Intent(INSTALL_ACTION)); 107 | } catch (ActivityNotFoundException e) { 108 | Log.w(LOGTAG, e.toString()); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/android/security/KeyStore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.security; 18 | 19 | import android.net.LocalSocketAddress; 20 | import android.net.LocalSocket; 21 | 22 | import java.io.InputStream; 23 | import java.io.IOException; 24 | import java.io.OutputStream; 25 | import java.io.UnsupportedEncodingException; 26 | import java.util.ArrayList; 27 | 28 | /** 29 | * {@hide} 30 | */ 31 | public class KeyStore { 32 | public static final int NO_ERROR = 1; 33 | public static final int LOCKED = 2; 34 | public static final int UNINITIALIZED = 3; 35 | public static final int SYSTEM_ERROR = 4; 36 | public static final int PROTOCOL_ERROR = 5; 37 | public static final int PERMISSION_DENIED = 6; 38 | public static final int KEY_NOT_FOUND = 7; 39 | public static final int VALUE_CORRUPTED = 8; 40 | public static final int UNDEFINED_ACTION = 9; 41 | public static final int WRONG_PASSWORD = 10; 42 | 43 | private static final LocalSocketAddress sAddress = new LocalSocketAddress( 44 | "keystore", LocalSocketAddress.Namespace.RESERVED); 45 | 46 | private int mError = NO_ERROR; 47 | 48 | private KeyStore() {} 49 | 50 | public static KeyStore getInstance() { 51 | return new KeyStore(); 52 | } 53 | 54 | public int test() { 55 | execute('t'); 56 | return mError; 57 | } 58 | 59 | public byte[] get(byte[] key) { 60 | ArrayList values = execute('g', key); 61 | return (values == null || values.isEmpty()) ? null : values.get(0); 62 | } 63 | 64 | public String get(String key) { 65 | byte[] value = get(getBytes(key)); 66 | return (value == null) ? null : toString(value); 67 | } 68 | 69 | public boolean put(byte[] key, byte[] value) { 70 | execute('i', key, value); 71 | return mError == NO_ERROR; 72 | } 73 | 74 | public boolean put(String key, String value) { 75 | return put(getBytes(key), getBytes(value)); 76 | } 77 | 78 | public boolean delete(byte[] key) { 79 | execute('d', key); 80 | return mError == NO_ERROR; 81 | } 82 | 83 | public boolean delete(String key) { 84 | return delete(getBytes(key)); 85 | } 86 | 87 | public boolean contains(byte[] key) { 88 | execute('e', key); 89 | return mError == NO_ERROR; 90 | } 91 | 92 | public boolean contains(String key) { 93 | return contains(getBytes(key)); 94 | } 95 | 96 | public byte[][] saw(byte[] prefix) { 97 | ArrayList values = execute('s', prefix); 98 | return (values == null) ? null : values.toArray(new byte[values.size()][]); 99 | } 100 | 101 | public String[] saw(String prefix) { 102 | byte[][] values = saw(getBytes(prefix)); 103 | if (values == null) { 104 | return null; 105 | } 106 | String[] strings = new String[values.length]; 107 | for (int i = 0; i < values.length; ++i) { 108 | strings[i] = toString(values[i]); 109 | } 110 | return strings; 111 | } 112 | 113 | public boolean reset() { 114 | execute('r'); 115 | return mError == NO_ERROR; 116 | } 117 | 118 | public boolean password(byte[] oldPassword, byte[] newPassword) { 119 | execute('p', oldPassword, newPassword); 120 | return mError == NO_ERROR; 121 | } 122 | 123 | public boolean password(String oldPassword, String newPassword) { 124 | return password(getBytes(oldPassword), getBytes(newPassword)); 125 | } 126 | 127 | public boolean password(byte[] password) { 128 | return password(password, password); 129 | } 130 | 131 | public boolean password(String password) { 132 | return password(getBytes(password)); 133 | } 134 | 135 | public boolean lock() { 136 | execute('l'); 137 | return mError == NO_ERROR; 138 | } 139 | 140 | public boolean unlock(byte[] password) { 141 | execute('u', password); 142 | return mError == NO_ERROR; 143 | } 144 | 145 | public boolean unlock(String password) { 146 | return unlock(getBytes(password)); 147 | } 148 | 149 | public int getLastError() { 150 | return mError; 151 | } 152 | 153 | private ArrayList execute(int code, byte[]... parameters) { 154 | mError = PROTOCOL_ERROR; 155 | 156 | for (byte[] parameter : parameters) { 157 | if (parameter == null || parameter.length > 65535) { 158 | return null; 159 | } 160 | } 161 | 162 | LocalSocket socket = new LocalSocket(); 163 | try { 164 | socket.connect(sAddress); 165 | 166 | OutputStream out = socket.getOutputStream(); 167 | out.write(code); 168 | for (byte[] parameter : parameters) { 169 | out.write(parameter.length >> 8); 170 | out.write(parameter.length); 171 | out.write(parameter); 172 | } 173 | out.flush(); 174 | socket.shutdownOutput(); 175 | 176 | InputStream in = socket.getInputStream(); 177 | if ((code = in.read()) != NO_ERROR) { 178 | if (code != -1) { 179 | mError = code; 180 | } 181 | return null; 182 | } 183 | 184 | ArrayList values = new ArrayList(); 185 | while (true) { 186 | int i, j; 187 | if ((i = in.read()) == -1) { 188 | break; 189 | } 190 | if ((j = in.read()) == -1) { 191 | return null; 192 | } 193 | byte[] value = new byte[i << 8 | j]; 194 | for (i = 0; i < value.length; i += j) { 195 | if ((j = in.read(value, i, value.length - i)) == -1) { 196 | return null; 197 | } 198 | } 199 | values.add(value); 200 | } 201 | mError = NO_ERROR; 202 | return values; 203 | } catch (IOException e) { 204 | // ignore 205 | } finally { 206 | try { 207 | socket.close(); 208 | } catch (IOException e) {} 209 | } 210 | return null; 211 | } 212 | 213 | private static byte[] getBytes(String string) { 214 | try { 215 | return string.getBytes("UTF-8"); 216 | } catch (UnsupportedEncodingException e) { 217 | // will never happen 218 | throw new RuntimeException(e); 219 | } 220 | } 221 | 222 | private static String toString(byte[] bytes) { 223 | try { 224 | return new String(bytes, "UTF-8"); 225 | } catch (UnsupportedEncodingException e) { 226 | // will never happen 227 | throw new RuntimeException(e); 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/com/android/server/vpn/DaemonProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import android.net.LocalSocket; 20 | import android.net.LocalSocketAddress; 21 | import android.net.vpn.VpnManager; 22 | import android.os.SystemProperties; 23 | import android.util.Log; 24 | 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | import java.io.OutputStream; 28 | import java.io.Serializable; 29 | 30 | /** 31 | * Proxy to start, stop and interact with a VPN daemon. 32 | * The daemon is expected to accept connection through Unix domain socket. 33 | * When the proxy successfully starts the daemon, it will establish a socket 34 | * connection with the daemon, to both send commands to the daemon and receive 35 | * response and connecting error code from the daemon. 36 | */ 37 | class DaemonProxy implements Serializable { 38 | private static final long serialVersionUID = 1L; 39 | private static final boolean DBG = true; 40 | 41 | private static final int WAITING_TIME = 15; // sec 42 | 43 | private static final String SVC_STATE_CMD_PREFIX = "init.svc."; 44 | private static final String SVC_START_CMD = "ctl.start"; 45 | private static final String SVC_STOP_CMD = "ctl.stop"; 46 | private static final String SVC_STATE_RUNNING = "running"; 47 | private static final String SVC_STATE_STOPPED = "stopped"; 48 | 49 | private static final int END_OF_ARGUMENTS = 255; 50 | 51 | private String mName; 52 | private String mTag; 53 | private transient LocalSocket mControlSocket; 54 | 55 | /** 56 | * Creates a proxy of the specified daemon. 57 | * @param daemonName name of the daemon 58 | */ 59 | DaemonProxy(String daemonName) { 60 | mName = daemonName; 61 | mTag = "SProxy_" + daemonName; 62 | } 63 | 64 | String getName() { 65 | return mName; 66 | } 67 | 68 | void start() throws IOException { 69 | String svc = mName; 70 | 71 | Log.i(mTag, "Start VPN daemon: " + svc); 72 | SystemProperties.set(SVC_START_CMD, svc); 73 | 74 | if (!blockUntil(SVC_STATE_RUNNING, WAITING_TIME)) { 75 | throw new IOException("cannot start service: " + svc); 76 | } else { 77 | mControlSocket = createServiceSocket(); 78 | } 79 | } 80 | 81 | void sendCommand(String ...args) throws IOException { 82 | OutputStream out = getControlSocketOutput(); 83 | for (String arg : args) outputString(out, arg); 84 | out.write(END_OF_ARGUMENTS); 85 | out.flush(); 86 | 87 | int result = getResultFromSocket(true); 88 | if (result != args.length) { 89 | throw new IOException("socket error, result from service: " 90 | + result); 91 | } 92 | } 93 | 94 | // returns 0 if nothing is in the receive buffer 95 | int getResultFromSocket() throws IOException { 96 | return getResultFromSocket(false); 97 | } 98 | 99 | void closeControlSocket() { 100 | if (mControlSocket == null) return; 101 | try { 102 | mControlSocket.close(); 103 | } catch (IOException e) { 104 | Log.w(mTag, "close control socket", e); 105 | } finally { 106 | mControlSocket = null; 107 | } 108 | } 109 | 110 | void stop() { 111 | String svc = mName; 112 | Log.i(mTag, "Stop VPN daemon: " + svc); 113 | SystemProperties.set(SVC_STOP_CMD, svc); 114 | boolean success = blockUntil(SVC_STATE_STOPPED, 5); 115 | if (DBG) Log.d(mTag, "stopping " + svc + ", success? " + success); 116 | } 117 | 118 | boolean isStopped() { 119 | String cmd = SVC_STATE_CMD_PREFIX + mName; 120 | return SVC_STATE_STOPPED.equals(SystemProperties.get(cmd)); 121 | } 122 | 123 | private int getResultFromSocket(boolean blocking) throws IOException { 124 | LocalSocket s = mControlSocket; 125 | if (s == null) return 0; 126 | InputStream in = s.getInputStream(); 127 | if (!blocking && in.available() == 0) return 0; 128 | 129 | int data = in.read(); 130 | Log.i(mTag, "got data from control socket: " + data); 131 | 132 | return data; 133 | } 134 | 135 | private LocalSocket createServiceSocket() throws IOException { 136 | LocalSocket s = new LocalSocket(); 137 | LocalSocketAddress a = new LocalSocketAddress(mName, 138 | LocalSocketAddress.Namespace.RESERVED); 139 | 140 | // try a few times in case the service has not listen()ed 141 | IOException excp = null; 142 | for (int i = 0; i < 10; i++) { 143 | try { 144 | s.connect(a); 145 | return s; 146 | } catch (IOException e) { 147 | if (DBG) Log.d(mTag, "service not yet listen()ing; try again"); 148 | excp = e; 149 | sleep(500); 150 | } 151 | } 152 | throw excp; 153 | } 154 | 155 | private OutputStream getControlSocketOutput() throws IOException { 156 | if (mControlSocket != null) { 157 | return mControlSocket.getOutputStream(); 158 | } else { 159 | throw new IOException("no control socket available"); 160 | } 161 | } 162 | 163 | /** 164 | * Waits for the process to be in the expected state. The method returns 165 | * false if after the specified duration (in seconds), the process is still 166 | * not in the expected state. 167 | */ 168 | private boolean blockUntil(String expectedState, int waitTime) { 169 | String cmd = SVC_STATE_CMD_PREFIX + mName; 170 | int sleepTime = 200; // ms 171 | int n = waitTime * 1000 / sleepTime; 172 | for (int i = 0; i < n; i++) { 173 | if (expectedState.equals(SystemProperties.get(cmd))) { 174 | if (DBG) { 175 | Log.d(mTag, mName + " is " + expectedState + " after " 176 | + (i * sleepTime) + " msec"); 177 | } 178 | break; 179 | } 180 | sleep(sleepTime); 181 | } 182 | return expectedState.equals(SystemProperties.get(cmd)); 183 | } 184 | 185 | private void outputString(OutputStream out, String s) throws IOException { 186 | byte[] bytes = s.getBytes(); 187 | out.write(bytes.length); 188 | out.write(bytes); 189 | out.flush(); 190 | } 191 | 192 | private void sleep(int msec) { 193 | try { 194 | Thread.currentThread().sleep(msec); 195 | } catch (InterruptedException e) { 196 | throw new RuntimeException(e); 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/com/android/server/vpn/L2tpIpsecPskService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import android.net.vpn.L2tpIpsecPskProfile; 20 | 21 | import java.io.IOException; 22 | 23 | /** 24 | * The service that manages the preshared key based L2TP-over-IPSec VPN 25 | * connection. 26 | */ 27 | class L2tpIpsecPskService extends VpnService { 28 | private static final String IPSEC = "racoon"; 29 | 30 | @Override 31 | protected void connect(String serverIp, String username, String password) 32 | throws IOException { 33 | L2tpIpsecPskProfile p = getProfile(); 34 | VpnDaemons daemons = getDaemons(); 35 | 36 | // IPSEC 37 | daemons.startIpsecForL2tp(serverIp, p.getPresharedKey()) 38 | .closeControlSocket(); 39 | 40 | sleep(2000); // 2 seconds 41 | 42 | // L2TP 43 | daemons.startL2tp(serverIp, 44 | (p.isSecretEnabled() ? p.getSecretString() : null), 45 | username, password); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/com/android/server/vpn/L2tpIpsecService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import android.net.vpn.L2tpIpsecProfile; 20 | import android.security.Credentials; 21 | 22 | import java.io.IOException; 23 | 24 | /** 25 | * The service that manages the certificate based L2TP-over-IPSec VPN connection. 26 | */ 27 | class L2tpIpsecService extends VpnService { 28 | private static final String IPSEC = "racoon"; 29 | 30 | @Override 31 | protected void connect(String serverIp, String username, String password) 32 | throws IOException { 33 | L2tpIpsecProfile p = getProfile(); 34 | VpnDaemons daemons = getDaemons(); 35 | 36 | // IPSEC 37 | DaemonProxy ipsec = daemons.startIpsecForL2tp(serverIp, 38 | Credentials.USER_PRIVATE_KEY + p.getUserCertificate(), 39 | Credentials.USER_CERTIFICATE + p.getUserCertificate(), 40 | Credentials.CA_CERTIFICATE + p.getCaCertificate()); 41 | ipsec.closeControlSocket(); 42 | 43 | sleep(2000); // 2 seconds 44 | 45 | // L2TP 46 | daemons.startL2tp(serverIp, 47 | (p.isSecretEnabled() ? p.getSecretString() : null), 48 | username, password); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/com/android/server/vpn/L2tpService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import android.net.vpn.L2tpProfile; 20 | 21 | import java.io.IOException; 22 | 23 | /** 24 | * The service that manages the L2TP VPN connection. 25 | */ 26 | class L2tpService extends VpnService { 27 | @Override 28 | protected void connect(String serverIp, String username, String password) 29 | throws IOException { 30 | L2tpProfile p = getProfile(); 31 | getDaemons().startL2tp(serverIp, 32 | (p.isSecretEnabled() ? p.getSecretString() : null), 33 | username, password); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/com/android/server/vpn/PptpService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import android.net.vpn.PptpProfile; 20 | 21 | import java.io.IOException; 22 | 23 | /** 24 | * The service that manages the PPTP VPN connection. 25 | */ 26 | class PptpService extends VpnService { 27 | @Override 28 | protected void connect(String serverIp, String username, String password) 29 | throws IOException { 30 | PptpProfile p = getProfile(); 31 | getDaemons().startPptp(serverIp, username, password, 32 | p.isEncryptionEnabled()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/com/android/server/vpn/VpnConnectingError.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import java.io.IOException; 20 | 21 | /** 22 | * Exception thrown when a connecting attempt fails. 23 | */ 24 | class VpnConnectingError extends IOException { 25 | private int mErrorCode; 26 | 27 | VpnConnectingError(int errorCode) { 28 | super("Connecting error: " + errorCode); 29 | mErrorCode = errorCode; 30 | } 31 | 32 | int getErrorCode() { 33 | return mErrorCode; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/com/android/server/vpn/VpnDaemons.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import android.util.Log; 20 | 21 | import java.io.IOException; 22 | import java.io.Serializable; 23 | import java.util.ArrayList; 24 | import java.util.Arrays; 25 | import java.util.List; 26 | 27 | /** 28 | * A helper class for managing native VPN daemons. 29 | */ 30 | class VpnDaemons implements Serializable { 31 | static final long serialVersionUID = 1L; 32 | private final String TAG = VpnDaemons.class.getSimpleName(); 33 | 34 | private static final String MTPD = "mtpd"; 35 | private static final String IPSEC = "racoon"; 36 | 37 | private static final String L2TP = "l2tp"; 38 | private static final String L2TP_PORT = "1701"; 39 | 40 | private static final String PPTP = "pptp"; 41 | private static final String PPTP_PORT = "1723"; 42 | 43 | private static final String VPN_LINKNAME = "vpn"; 44 | private static final String PPP_ARGS_SEPARATOR = ""; 45 | 46 | private List mDaemonList = new ArrayList(); 47 | 48 | public DaemonProxy startL2tp(String serverIp, String secret, 49 | String username, String password) throws IOException { 50 | return startMtpd(L2TP, serverIp, L2TP_PORT, secret, username, password, 51 | false); 52 | } 53 | 54 | public DaemonProxy startPptp(String serverIp, String username, 55 | String password, boolean encryption) throws IOException { 56 | return startMtpd(PPTP, serverIp, PPTP_PORT, null, username, password, 57 | encryption); 58 | } 59 | 60 | public DaemonProxy startIpsecForL2tp(String serverIp, String pskKey) 61 | throws IOException { 62 | DaemonProxy ipsec = startDaemon(IPSEC); 63 | ipsec.sendCommand(serverIp, L2TP_PORT, pskKey); 64 | return ipsec; 65 | } 66 | 67 | public DaemonProxy startIpsecForL2tp(String serverIp, String userKeyKey, 68 | String userCertKey, String caCertKey) throws IOException { 69 | DaemonProxy ipsec = startDaemon(IPSEC); 70 | ipsec.sendCommand(serverIp, L2TP_PORT, userKeyKey, userCertKey, 71 | caCertKey); 72 | return ipsec; 73 | } 74 | 75 | public synchronized void stopAll() { 76 | new DaemonProxy(MTPD).stop(); 77 | new DaemonProxy(IPSEC).stop(); 78 | } 79 | 80 | public synchronized void closeSockets() { 81 | for (DaemonProxy s : mDaemonList) s.closeControlSocket(); 82 | } 83 | 84 | public synchronized boolean anyDaemonStopped() { 85 | for (DaemonProxy s : mDaemonList) { 86 | if (s.isStopped()) { 87 | Log.w(TAG, " VPN daemon gone: " + s.getName()); 88 | return true; 89 | } 90 | } 91 | return false; 92 | } 93 | 94 | public synchronized int getSocketError() { 95 | for (DaemonProxy s : mDaemonList) { 96 | int errCode = getResultFromSocket(s); 97 | if (errCode != 0) return errCode; 98 | } 99 | return 0; 100 | } 101 | 102 | private synchronized DaemonProxy startDaemon(String daemonName) 103 | throws IOException { 104 | DaemonProxy daemon = new DaemonProxy(daemonName); 105 | mDaemonList.add(daemon); 106 | daemon.start(); 107 | return daemon; 108 | } 109 | 110 | private int getResultFromSocket(DaemonProxy s) { 111 | try { 112 | return s.getResultFromSocket(); 113 | } catch (IOException e) { 114 | return -1; 115 | } 116 | } 117 | 118 | private DaemonProxy startMtpd(String protocol, 119 | String serverIp, String port, String secret, String username, 120 | String password, boolean encryption) throws IOException { 121 | ArrayList args = new ArrayList(); 122 | args.addAll(Arrays.asList(protocol, serverIp, port)); 123 | if (secret != null) args.add(secret); 124 | args.add(PPP_ARGS_SEPARATOR); 125 | addPppArguments(args, serverIp, username, password, encryption); 126 | 127 | DaemonProxy mtpd = startDaemon(MTPD); 128 | mtpd.sendCommand(args.toArray(new String[args.size()])); 129 | return mtpd; 130 | } 131 | 132 | private static void addPppArguments(ArrayList args, String serverIp, 133 | String username, String password, boolean encryption) 134 | throws IOException { 135 | args.addAll(Arrays.asList( 136 | "linkname", VPN_LINKNAME, 137 | "name", username, 138 | "password", password, 139 | "refuse-eap", "nodefaultroute", "usepeerdns", 140 | "idle", "1800", 141 | "mtu", "1400", 142 | "mru", "1400")); 143 | if (encryption) { 144 | args.add("+mppe"); 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/com/android/server/vpn/VpnService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import com.android.server.vpn.R; 20 | 21 | import java.io.IOException; 22 | import java.io.Serializable; 23 | import java.net.DatagramSocket; 24 | import java.net.InetAddress; 25 | import java.net.NetworkInterface; 26 | import java.net.UnknownHostException; 27 | 28 | import android.app.Notification; 29 | import android.app.NotificationManager; 30 | import android.app.PendingIntent; 31 | import android.content.Context; 32 | import android.net.vpn.VpnManager; 33 | import android.net.vpn.VpnProfile; 34 | import android.net.vpn.VpnState; 35 | import android.os.SystemProperties; 36 | import android.text.TextUtils; 37 | import android.util.Log; 38 | 39 | /** 40 | * The service base class for managing a type of VPN connection. 41 | */ 42 | abstract class VpnService implements Serializable { 43 | static final long serialVersionUID = 1L; 44 | private static final boolean DBG = true; 45 | private static final int NOTIFICATION_ID = 1; 46 | 47 | private static final String DNS1 = "net.dns1"; 48 | private static final String DNS2 = "net.dns2"; 49 | private static final String VPN_DNS1 = "vpn.dns1"; 50 | private static final String VPN_DNS2 = "vpn.dns2"; 51 | private static final String VPN_STATUS = "vpn.status"; 52 | private static final String VPN_IS_UP = "ok"; 53 | private static final String VPN_IS_DOWN = "down"; 54 | 55 | private static final String REMOTE_IP = "net.ipremote"; 56 | private static final String DNS_DOMAIN_SUFFICES = "net.dns.search"; 57 | 58 | private final String TAG = VpnService.class.getSimpleName(); 59 | 60 | // FIXME: profile is only needed in connecting phase, so we can just save 61 | // the profile name and service class name for recovery 62 | E mProfile; 63 | transient VpnServiceBinder mContext; 64 | 65 | private VpnState mState = VpnState.IDLE; 66 | private Throwable mError; 67 | 68 | // connection settings 69 | private String mOriginalDns1; 70 | private String mOriginalDns2; 71 | private String mOriginalDomainSuffices; 72 | private String mLocalIp; 73 | private String mLocalIf; 74 | 75 | private long mStartTime; // VPN connection start time 76 | 77 | // for helping managing daemons 78 | private VpnDaemons mDaemons = new VpnDaemons(); 79 | 80 | // for helping showing, updating notification 81 | private transient NotificationHelper mNotification; 82 | 83 | /** 84 | * Establishes a VPN connection with the specified username and password. 85 | */ 86 | protected abstract void connect(String serverIp, String username, 87 | String password) throws IOException; 88 | 89 | /** 90 | * Returns the daemons management class for this service object. 91 | */ 92 | protected VpnDaemons getDaemons() { 93 | return mDaemons; 94 | } 95 | 96 | /** 97 | * Returns the VPN profile associated with the connection. 98 | */ 99 | protected E getProfile() { 100 | return mProfile; 101 | } 102 | 103 | /** 104 | * Returns the IP address of the specified host name. 105 | */ 106 | protected String getIp(String hostName) throws IOException { 107 | return InetAddress.getByName(hostName).getHostAddress(); 108 | } 109 | 110 | void setContext(VpnServiceBinder context, E profile) { 111 | mProfile = profile; 112 | recover(context); 113 | } 114 | 115 | void recover(VpnServiceBinder context) { 116 | mContext = context; 117 | mNotification = new NotificationHelper(); 118 | 119 | if (VpnState.CONNECTED.equals(mState)) { 120 | Log.i("VpnService", " recovered: " + mProfile.getName()); 121 | startConnectivityMonitor(); 122 | } 123 | } 124 | 125 | VpnState getState() { 126 | return mState; 127 | } 128 | 129 | synchronized boolean onConnect(String username, String password) { 130 | try { 131 | setState(VpnState.CONNECTING); 132 | 133 | mDaemons.stopAll(); 134 | String serverIp = getIp(getProfile().getServerName()); 135 | saveLocalIpAndInterface(serverIp); 136 | onBeforeConnect(); 137 | connect(serverIp, username, password); 138 | waitUntilConnectedOrTimedout(); 139 | return true; 140 | } catch (Throwable e) { 141 | onError(e); 142 | return false; 143 | } 144 | } 145 | 146 | synchronized void onDisconnect() { 147 | try { 148 | Log.i(TAG, "disconnecting VPN..."); 149 | setState(VpnState.DISCONNECTING); 150 | mNotification.showDisconnect(); 151 | 152 | mDaemons.stopAll(); 153 | } catch (Throwable e) { 154 | Log.e(TAG, "onDisconnect()", e); 155 | } finally { 156 | onFinalCleanUp(); 157 | } 158 | } 159 | 160 | private void onError(Throwable error) { 161 | // error may occur during or after connection setup 162 | // and it may be due to one or all services gone 163 | if (mError != null) { 164 | Log.w(TAG, " multiple errors occur, record the last one: " 165 | + error); 166 | } 167 | Log.e(TAG, "onError()", error); 168 | mError = error; 169 | onDisconnect(); 170 | } 171 | 172 | private void onError(int errorCode) { 173 | onError(new VpnConnectingError(errorCode)); 174 | } 175 | 176 | 177 | private void onBeforeConnect() throws IOException { 178 | mNotification.disableNotification(); 179 | 180 | SystemProperties.set(VPN_DNS1, ""); 181 | SystemProperties.set(VPN_DNS2, ""); 182 | SystemProperties.set(VPN_STATUS, VPN_IS_DOWN); 183 | if (DBG) { 184 | Log.d(TAG, " VPN UP: " + SystemProperties.get(VPN_STATUS)); 185 | } 186 | } 187 | 188 | private void waitUntilConnectedOrTimedout() throws IOException { 189 | sleep(2000); // 2 seconds 190 | for (int i = 0; i < 80; i++) { 191 | if (mState != VpnState.CONNECTING) { 192 | break; 193 | } else if (VPN_IS_UP.equals( 194 | SystemProperties.get(VPN_STATUS))) { 195 | onConnected(); 196 | return; 197 | } else { 198 | int err = mDaemons.getSocketError(); 199 | if (err != 0) { 200 | onError(err); 201 | return; 202 | } 203 | } 204 | sleep(500); // 0.5 second 205 | } 206 | 207 | if (mState == VpnState.CONNECTING) { 208 | onError(new IOException("Connecting timed out")); 209 | } 210 | } 211 | 212 | private synchronized void onConnected() throws IOException { 213 | if (DBG) Log.d(TAG, "onConnected()"); 214 | 215 | mDaemons.closeSockets(); 216 | saveOriginalDns(); 217 | saveAndSetDomainSuffices(); 218 | 219 | mStartTime = System.currentTimeMillis(); 220 | 221 | // Correct order to make sure VpnService doesn't break when killed: 222 | // (1) set state to CONNECTED 223 | // (2) save states 224 | // (3) set DNS 225 | setState(VpnState.CONNECTED); 226 | saveSelf(); 227 | setVpnDns(); 228 | 229 | startConnectivityMonitor(); 230 | } 231 | 232 | private void saveSelf() throws IOException { 233 | mContext.saveStates(); 234 | } 235 | 236 | private synchronized void onFinalCleanUp() { 237 | if (DBG) Log.d(TAG, "onFinalCleanUp()"); 238 | 239 | if (mState == VpnState.IDLE) return; 240 | 241 | // keep the notification when error occurs 242 | if (!anyError()) mNotification.disableNotification(); 243 | 244 | restoreOriginalDns(); 245 | restoreOriginalDomainSuffices(); 246 | setState(VpnState.IDLE); 247 | 248 | // stop the service itself 249 | SystemProperties.set(VPN_STATUS, VPN_IS_DOWN); 250 | mContext.removeStates(); 251 | mContext.stopSelf(); 252 | } 253 | 254 | private boolean anyError() { 255 | return (mError != null); 256 | } 257 | 258 | private void restoreOriginalDns() { 259 | // restore only if they are not overridden 260 | String vpnDns1 = SystemProperties.get(VPN_DNS1); 261 | if (vpnDns1.equals(SystemProperties.get(DNS1))) { 262 | Log.i(TAG, String.format("restore original dns prop: %s --> %s", 263 | SystemProperties.get(DNS1), mOriginalDns1)); 264 | Log.i(TAG, String.format("restore original dns prop: %s --> %s", 265 | SystemProperties.get(DNS2), mOriginalDns2)); 266 | SystemProperties.set(DNS1, mOriginalDns1); 267 | SystemProperties.set(DNS2, mOriginalDns2); 268 | } 269 | } 270 | 271 | private void saveOriginalDns() { 272 | mOriginalDns1 = SystemProperties.get(DNS1); 273 | mOriginalDns2 = SystemProperties.get(DNS2); 274 | Log.i(TAG, String.format("save original dns prop: %s, %s", 275 | mOriginalDns1, mOriginalDns2)); 276 | } 277 | 278 | private void setVpnDns() { 279 | String vpnDns1 = SystemProperties.get(VPN_DNS1); 280 | String vpnDns2 = SystemProperties.get(VPN_DNS2); 281 | SystemProperties.set(DNS1, vpnDns1); 282 | SystemProperties.set(DNS2, vpnDns2); 283 | Log.i(TAG, String.format("set vpn dns prop: %s, %s", 284 | vpnDns1, vpnDns2)); 285 | } 286 | 287 | private void saveAndSetDomainSuffices() { 288 | mOriginalDomainSuffices = SystemProperties.get(DNS_DOMAIN_SUFFICES); 289 | Log.i(TAG, "save original suffices: " + mOriginalDomainSuffices); 290 | String list = mProfile.getDomainSuffices(); 291 | if (!TextUtils.isEmpty(list)) { 292 | SystemProperties.set(DNS_DOMAIN_SUFFICES, list); 293 | } 294 | } 295 | 296 | private void restoreOriginalDomainSuffices() { 297 | Log.i(TAG, "restore original suffices --> " + mOriginalDomainSuffices); 298 | SystemProperties.set(DNS_DOMAIN_SUFFICES, mOriginalDomainSuffices); 299 | } 300 | 301 | private void setState(VpnState newState) { 302 | mState = newState; 303 | broadcastConnectivity(newState); 304 | } 305 | 306 | private void broadcastConnectivity(VpnState s) { 307 | VpnManager m = new VpnManager(mContext); 308 | Throwable err = mError; 309 | if ((s == VpnState.IDLE) && (err != null)) { 310 | if (err instanceof UnknownHostException) { 311 | m.broadcastConnectivity(mProfile.getName(), s, 312 | VpnManager.VPN_ERROR_UNKNOWN_SERVER); 313 | } else if (err instanceof VpnConnectingError) { 314 | m.broadcastConnectivity(mProfile.getName(), s, 315 | ((VpnConnectingError) err).getErrorCode()); 316 | } else if (VPN_IS_UP.equals(SystemProperties.get(VPN_STATUS))) { 317 | m.broadcastConnectivity(mProfile.getName(), s, 318 | VpnManager.VPN_ERROR_CONNECTION_LOST); 319 | } else { 320 | m.broadcastConnectivity(mProfile.getName(), s, 321 | VpnManager.VPN_ERROR_CONNECTION_FAILED); 322 | } 323 | } else { 324 | m.broadcastConnectivity(mProfile.getName(), s); 325 | } 326 | } 327 | 328 | private void startConnectivityMonitor() { 329 | new Thread(new Runnable() { 330 | public void run() { 331 | Log.i(TAG, "VPN connectivity monitor running"); 332 | try { 333 | for (int i = 10; ; i--) { 334 | long now = System.currentTimeMillis(); 335 | 336 | boolean heavyCheck = i == 0; 337 | synchronized (VpnService.this) { 338 | if (mState != VpnState.CONNECTED) break; 339 | mNotification.update(now); 340 | 341 | if (heavyCheck) { 342 | i = 10; 343 | if (checkConnectivity()) checkDns(); 344 | } 345 | long t = 1000L - System.currentTimeMillis() + now; 346 | if (t > 100L) VpnService.this.wait(t); 347 | } 348 | } 349 | } catch (InterruptedException e) { 350 | onError(e); 351 | } 352 | Log.i(TAG, "VPN connectivity monitor stopped"); 353 | } 354 | }).start(); 355 | } 356 | 357 | private void saveLocalIpAndInterface(String serverIp) throws IOException { 358 | DatagramSocket s = new DatagramSocket(); 359 | int port = 80; // arbitrary 360 | s.connect(InetAddress.getByName(serverIp), port); 361 | InetAddress localIp = s.getLocalAddress(); 362 | mLocalIp = localIp.getHostAddress(); 363 | NetworkInterface localIf = NetworkInterface.getByInetAddress(localIp); 364 | mLocalIf = (localIf == null) ? null : localIf.getName(); 365 | if (TextUtils.isEmpty(mLocalIf)) { 366 | throw new IOException("Local interface is empty!"); 367 | } 368 | if (DBG) { 369 | Log.d(TAG, " Local IP: " + mLocalIp + ", if: " + mLocalIf); 370 | } 371 | } 372 | 373 | // returns false if vpn connectivity is broken 374 | private boolean checkConnectivity() { 375 | if (mDaemons.anyDaemonStopped() || isLocalIpChanged()) { 376 | onError(new IOException("Connectivity lost")); 377 | return false; 378 | } else { 379 | return true; 380 | } 381 | } 382 | 383 | private void checkDns() { 384 | String dns1 = SystemProperties.get(DNS1); 385 | String vpnDns1 = SystemProperties.get(VPN_DNS1); 386 | if (!dns1.equals(vpnDns1) && dns1.equals(mOriginalDns1)) { 387 | // dhcp expires? 388 | setVpnDns(); 389 | } 390 | } 391 | 392 | private boolean isLocalIpChanged() { 393 | try { 394 | InetAddress localIp = InetAddress.getByName(mLocalIp); 395 | NetworkInterface localIf = 396 | NetworkInterface.getByInetAddress(localIp); 397 | if (localIf == null || !mLocalIf.equals(localIf.getName())) { 398 | Log.w(TAG, " local If changed from " + mLocalIf 399 | + " to " + localIf); 400 | return true; 401 | } else { 402 | return false; 403 | } 404 | } catch (IOException e) { 405 | Log.w(TAG, "isLocalIpChanged()", e); 406 | return true; 407 | } 408 | } 409 | 410 | protected void sleep(int ms) { 411 | try { 412 | Thread.currentThread().sleep(ms); 413 | } catch (InterruptedException e) { 414 | } 415 | } 416 | 417 | private class DaemonHelper implements Serializable { 418 | } 419 | 420 | // Helper class for showing, updating notification. 421 | private class NotificationHelper { 422 | void update(long now) { 423 | String title = getNotificationTitle(true); 424 | Notification n = new Notification(R.drawable.vpn_connected, title, 425 | mStartTime); 426 | n.setLatestEventInfo(mContext, title, 427 | getConnectedNotificationMessage(now), 428 | prepareNotificationIntent()); 429 | n.flags |= Notification.FLAG_NO_CLEAR; 430 | n.flags |= Notification.FLAG_ONGOING_EVENT; 431 | enableNotification(n); 432 | } 433 | 434 | void showDisconnect() { 435 | String title = getNotificationTitle(false); 436 | Notification n = new Notification(R.drawable.vpn_disconnected, 437 | title, System.currentTimeMillis()); 438 | n.setLatestEventInfo(mContext, title, 439 | getDisconnectedNotificationMessage(), 440 | prepareNotificationIntent()); 441 | n.flags |= Notification.FLAG_AUTO_CANCEL; 442 | disableNotification(); 443 | enableNotification(n); 444 | } 445 | 446 | void disableNotification() { 447 | ((NotificationManager) mContext.getSystemService( 448 | Context.NOTIFICATION_SERVICE)).cancel(NOTIFICATION_ID); 449 | } 450 | 451 | private void enableNotification(Notification n) { 452 | ((NotificationManager) mContext.getSystemService( 453 | Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, n); 454 | } 455 | 456 | private PendingIntent prepareNotificationIntent() { 457 | return PendingIntent.getActivity(mContext, 0, 458 | new VpnManager(mContext).createSettingsActivityIntent(), 0); 459 | } 460 | 461 | private String getNotificationTitle(boolean connected) { 462 | String formatString = connected 463 | ? mContext.getString( 464 | R.string.vpn_notification_title_connected) 465 | : mContext.getString( 466 | R.string.vpn_notification_title_disconnected); 467 | return String.format(formatString, mProfile.getName()); 468 | } 469 | 470 | private String getFormattedTime(int duration) { 471 | int hours = duration / 3600; 472 | StringBuilder sb = new StringBuilder(); 473 | if (hours > 0) sb.append(hours).append(':'); 474 | sb.append(String.format("%02d:%02d", (duration % 3600 / 60), 475 | (duration % 60))); 476 | return sb.toString(); 477 | } 478 | 479 | private String getConnectedNotificationMessage(long now) { 480 | return getFormattedTime((int) (now - mStartTime) / 1000); 481 | } 482 | 483 | private String getDisconnectedNotificationMessage() { 484 | return mContext.getString( 485 | R.string.vpn_notification_hint_disconnected); 486 | } 487 | } 488 | } 489 | -------------------------------------------------------------------------------- /src/com/android/server/vpn/VpnServiceBinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.server.vpn; 18 | 19 | import android.app.Service; 20 | import android.content.Intent; 21 | import android.net.vpn.IVpnService; 22 | import android.net.vpn.L2tpIpsecProfile; 23 | import android.net.vpn.L2tpIpsecPskProfile; 24 | import android.net.vpn.L2tpProfile; 25 | import android.net.vpn.PptpProfile; 26 | import android.net.vpn.VpnManager; 27 | import android.net.vpn.VpnProfile; 28 | import android.net.vpn.VpnState; 29 | import android.os.Environment; 30 | import android.os.IBinder; 31 | import android.os.SystemProperties; 32 | import android.util.Log; 33 | 34 | import java.io.File; 35 | import java.io.FileInputStream; 36 | import java.io.FileNotFoundException; 37 | import java.io.FileOutputStream; 38 | import java.io.IOException; 39 | import java.io.ObjectInputStream; 40 | import java.io.ObjectOutputStream; 41 | 42 | /** 43 | * The service class for managing a VPN connection. It implements the 44 | * {@link IVpnService} binder interface. 45 | */ 46 | public class VpnServiceBinder extends Service { 47 | private static final String TAG = VpnServiceBinder.class.getSimpleName(); 48 | private static final boolean DBG = true; 49 | 50 | private static final String STATES_FILE_RELATIVE_PATH = "/misc/vpn/.states"; 51 | 52 | // The actual implementation is delegated to the VpnService class. 53 | private VpnService mService; 54 | 55 | private static String getStateFilePath() { 56 | return Environment.getDataDirectory().getPath() + STATES_FILE_RELATIVE_PATH; 57 | } 58 | 59 | private final IBinder mBinder = new IVpnService.Stub() { 60 | public boolean connect(VpnProfile p, String username, String password) { 61 | return VpnServiceBinder.this.connect(p, username, password); 62 | } 63 | 64 | public void disconnect() { 65 | VpnServiceBinder.this.disconnect(); 66 | } 67 | 68 | public void checkStatus(VpnProfile p) { 69 | VpnServiceBinder.this.checkStatus(p); 70 | } 71 | }; 72 | 73 | @Override 74 | public void onCreate() { 75 | super.onCreate(); 76 | checkSavedStates(); 77 | } 78 | 79 | 80 | @Override 81 | public void onStart(Intent intent, int startId) { 82 | super.onStart(intent, startId); 83 | } 84 | 85 | @Override 86 | public IBinder onBind(Intent intent) { 87 | return mBinder; 88 | } 89 | 90 | void saveStates() throws IOException { 91 | if (DBG) Log.d("VpnServiceBinder", " saving states"); 92 | ObjectOutputStream oos = 93 | new ObjectOutputStream(new FileOutputStream(getStateFilePath())); 94 | oos.writeObject(mService); 95 | oos.close(); 96 | } 97 | 98 | void removeStates() { 99 | try { 100 | File f = new File(getStateFilePath()); 101 | if (f.exists()) f.delete(); 102 | } catch (Throwable e) { 103 | if (DBG) Log.d("VpnServiceBinder", " remove states: " + e); 104 | } 105 | } 106 | 107 | private synchronized boolean connect(final VpnProfile p, 108 | final String username, final String password) { 109 | if (mService != null) return false; 110 | final VpnService s = mService = createService(p); 111 | 112 | new Thread(new Runnable() { 113 | public void run() { 114 | s.onConnect(username, password); 115 | } 116 | }).start(); 117 | return true; 118 | } 119 | 120 | private synchronized void disconnect() { 121 | if (mService == null) return; 122 | final VpnService s = mService; 123 | 124 | new Thread(new Runnable() { 125 | public void run() { 126 | s.onDisconnect(); 127 | } 128 | }).start(); 129 | } 130 | 131 | private synchronized void checkStatus(VpnProfile p) { 132 | if ((mService == null) 133 | || (!p.getName().equals(mService.mProfile.getName()))) { 134 | broadcastConnectivity(p.getName(), VpnState.IDLE); 135 | } else { 136 | broadcastConnectivity(p.getName(), mService.getState()); 137 | } 138 | } 139 | 140 | private void checkSavedStates() { 141 | try { 142 | ObjectInputStream ois = new ObjectInputStream(new FileInputStream( 143 | getStateFilePath())); 144 | mService = (VpnService) ois.readObject(); 145 | mService.recover(this); 146 | ois.close(); 147 | } catch (FileNotFoundException e) { 148 | // do nothing 149 | } catch (Throwable e) { 150 | Log.i("VpnServiceBinder", "recovery error, remove states: " + e); 151 | removeStates(); 152 | } 153 | } 154 | 155 | private VpnService createService(VpnProfile p) { 156 | switch (p.getType()) { 157 | case L2TP: 158 | L2tpService l2tp = new L2tpService(); 159 | l2tp.setContext(this, (L2tpProfile) p); 160 | return l2tp; 161 | 162 | case PPTP: 163 | PptpService pptp = new PptpService(); 164 | pptp.setContext(this, (PptpProfile) p); 165 | return pptp; 166 | 167 | case L2TP_IPSEC_PSK: 168 | L2tpIpsecPskService psk = new L2tpIpsecPskService(); 169 | psk.setContext(this, (L2tpIpsecPskProfile) p); 170 | return psk; 171 | 172 | case L2TP_IPSEC: 173 | L2tpIpsecService l2tpIpsec = new L2tpIpsecService(); 174 | l2tpIpsec.setContext(this, (L2tpIpsecProfile) p); 175 | return l2tpIpsec; 176 | 177 | default: 178 | return null; 179 | } 180 | } 181 | 182 | private void broadcastConnectivity(String name, VpnState s) { 183 | new VpnManager(this).broadcastConnectivity(name, s); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/org/zju/luojs/MyVpn.java: -------------------------------------------------------------------------------- 1 | package org.zju.luojs; 2 | 3 | import com.android.server.vpn.R; 4 | import android.app.Activity; 5 | import android.os.Bundle; 6 | import android.widget.TextView; 7 | 8 | public class MyVpn extends Activity { 9 | /** Called when the activity is first created. */ 10 | @Override 11 | public void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.main); 14 | } 15 | } --------------------------------------------------------------------------------