blob: 2fc972fc215205ae7dc713fa9a7822e3916e13c5 [file] [log] [blame]
Chia-chi Yehff3bdca2011-05-23 17:26:46 -07001/*
2 * Copyright (C) 2011 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
17package com.android.server.connectivity;
18
Jeff Sharkey899223b2012-08-04 15:24:58 -070019import static android.Manifest.permission.BIND_VPN_SERVICE;
20
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070021import android.app.Notification;
22import android.app.NotificationManager;
Jeff Sharkey899223b2012-08-04 15:24:58 -070023import android.app.PendingIntent;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -070024import android.content.BroadcastReceiver;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070025import android.content.ComponentName;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070026import android.content.Context;
27import android.content.Intent;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -070028import android.content.IntentFilter;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070029import android.content.ServiceConnection;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070030import android.content.pm.ApplicationInfo;
31import android.content.pm.PackageManager;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070032import android.content.pm.ResolveInfo;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070033import android.graphics.Bitmap;
34import android.graphics.Canvas;
35import android.graphics.drawable.Drawable;
Jeff Sharkey899223b2012-08-04 15:24:58 -070036import android.net.BaseNetworkStateTracker;
37import android.net.ConnectivityManager;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -070038import android.net.IConnectivityManager;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070039import android.net.INetworkManagementEventObserver;
Jeff Sharkey82f85212012-08-24 11:17:25 -070040import android.net.LinkProperties;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070041import android.net.LocalSocket;
42import android.net.LocalSocketAddress;
Jeff Sharkey899223b2012-08-04 15:24:58 -070043import android.net.NetworkInfo;
Jeff Sharkey82f85212012-08-24 11:17:25 -070044import android.net.RouteInfo;
Jeff Sharkey899223b2012-08-04 15:24:58 -070045import android.net.NetworkInfo.DetailedState;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070046import android.os.Binder;
Chia-chi Yehc1bac3a2011-12-16 15:00:31 -080047import android.os.FileUtils;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070048import android.os.IBinder;
Jeff Sharkey899223b2012-08-04 15:24:58 -070049import android.os.INetworkManagementService;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070050import android.os.Parcel;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070051import android.os.ParcelFileDescriptor;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070052import android.os.Process;
Jeff Sharkey899223b2012-08-04 15:24:58 -070053import android.os.RemoteException;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070054import android.os.SystemClock;
Jeff Sharkey088f29f2012-08-05 14:55:04 -070055import android.os.SystemService;
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -070056import android.os.UserHandle;
Jeff Sharkey82f85212012-08-24 11:17:25 -070057import android.security.Credentials;
58import android.security.KeyStore;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070059import android.util.Log;
Jeff Sharkey82f85212012-08-24 11:17:25 -070060import android.widget.Toast;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070061
62import com.android.internal.R;
Chia-chi Yeh2e467642011-07-04 03:23:12 -070063import com.android.internal.net.LegacyVpnInfo;
Chia-chi Yeh04ba25c2011-06-15 17:07:27 -070064import com.android.internal.net.VpnConfig;
Jeff Sharkey82f85212012-08-24 11:17:25 -070065import com.android.internal.net.VpnProfile;
Jeff Sharkey899223b2012-08-04 15:24:58 -070066import com.android.internal.util.Preconditions;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070067import com.android.server.ConnectivityService.VpnCallback;
Jeff Sharkey899223b2012-08-04 15:24:58 -070068import com.android.server.net.BaseNetworkObserver;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070069
Chia-chi Yeh97a61562011-07-14 15:05:05 -070070import java.io.File;
Chia-chi Yeh97a61562011-07-14 15:05:05 -070071import java.io.InputStream;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070072import java.io.OutputStream;
Jeff Sharkey82f85212012-08-24 11:17:25 -070073import java.net.Inet4Address;
74import java.net.InetAddress;
Elliott Hughesd396a442013-06-28 16:24:48 -070075import java.nio.charset.StandardCharsets;
Chia-chi Yeh41d16852011-07-01 02:12:06 -070076import java.util.Arrays;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -070077import java.util.concurrent.atomic.AtomicInteger;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070078
Jeff Sharkey065b2992012-08-05 14:16:48 -070079import libcore.io.IoUtils;
80
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070081/**
82 * @hide
83 */
Jeff Sharkey899223b2012-08-04 15:24:58 -070084public class Vpn extends BaseNetworkStateTracker {
85 private static final String TAG = "Vpn";
86 private static final boolean LOGD = true;
87
88 // TODO: create separate trackers for each unique VPN to support
89 // automated reconnection
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070090
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070091 private final VpnCallback mCallback;
92
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -070093 private String mPackage = VpnConfig.LEGACY_VPN;
94 private String mInterface;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070095 private Connection mConnection;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070096 private LegacyVpnRunner mLegacyVpnRunner;
Jeff Sharkey899223b2012-08-04 15:24:58 -070097 private PendingIntent mStatusIntent;
Jeff Sharkey57666932013-04-30 17:01:57 -070098 private volatile boolean mEnableNotif = true;
99 private volatile boolean mEnableTeardown = true;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700100 private final IConnectivityManager mConnService;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700101
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700102 public Vpn(Context context, VpnCallback callback, INetworkManagementService netService,
103 IConnectivityManager connService) {
Jeff Sharkey899223b2012-08-04 15:24:58 -0700104 // TODO: create dedicated TYPE_VPN network type
105 super(ConnectivityManager.TYPE_DUMMY);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700106 mContext = context;
107 mCallback = callback;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700108 mConnService = connService;
Jeff Sharkey899223b2012-08-04 15:24:58 -0700109
110 try {
111 netService.registerObserver(mObserver);
112 } catch (RemoteException e) {
113 Log.wtf(TAG, "Problem registering observer", e);
114 }
115 }
116
Jeff Sharkey57666932013-04-30 17:01:57 -0700117 /**
118 * Set if this object is responsible for showing its own notifications. When
119 * {@code false}, notifications are handled externally by someone else.
120 */
Jeff Sharkey69ddab42012-08-25 00:05:46 -0700121 public void setEnableNotifications(boolean enableNotif) {
122 mEnableNotif = enableNotif;
123 }
124
Jeff Sharkey57666932013-04-30 17:01:57 -0700125 /**
126 * Set if this object is responsible for watching for {@link NetworkInfo}
127 * teardown. When {@code false}, teardown is handled externally by someone
128 * else.
129 */
130 public void setEnableTeardown(boolean enableTeardown) {
131 mEnableTeardown = enableTeardown;
132 }
133
Jeff Sharkey899223b2012-08-04 15:24:58 -0700134 @Override
135 protected void startMonitoringInternal() {
136 // Ignored; events are sent through callbacks for now
137 }
138
139 @Override
140 public boolean teardown() {
141 // TODO: finish migration to unique tracker for each VPN
142 throw new UnsupportedOperationException();
143 }
144
145 @Override
146 public boolean reconnect() {
147 // TODO: finish migration to unique tracker for each VPN
148 throw new UnsupportedOperationException();
149 }
150
151 @Override
152 public String getTcpBufferSizesPropName() {
153 return PROP_TCP_BUFFER_UNKNOWN;
154 }
155
156 /**
157 * Update current state, dispaching event to listeners.
158 */
159 private void updateState(DetailedState detailedState, String reason) {
160 if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
161 mNetworkInfo.setDetailedState(detailedState, reason, null);
162 mCallback.onStateChanged(new NetworkInfo(mNetworkInfo));
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700163 }
164
165 /**
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700166 * Prepare for a VPN application. This method is designed to solve
167 * race conditions. It first compares the current prepared package
168 * with {@code oldPackage}. If they are the same, the prepared
169 * package is revoked and replaced with {@code newPackage}. If
170 * {@code oldPackage} is {@code null}, the comparison is omitted.
171 * If {@code newPackage} is the same package or {@code null}, the
172 * revocation is omitted. This method returns {@code true} if the
173 * operation is succeeded.
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700174 *
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700175 * Legacy VPN is handled specially since it is not a real package.
176 * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
177 * it can be revoked by itself.
178 *
179 * @param oldPackage The package name of the old VPN application.
180 * @param newPackage The package name of the new VPN application.
181 * @return true if the operation is succeeded.
Chia-chi Yehe9107902011-07-02 01:48:50 -0700182 */
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700183 public synchronized boolean prepare(String oldPackage, String newPackage) {
184 // Return false if the package does not match.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700185 if (oldPackage != null && !oldPackage.equals(mPackage)) {
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700186 return false;
Chia-chi Yehe9107902011-07-02 01:48:50 -0700187 }
188
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700189 // Return true if we do not need to revoke.
190 if (newPackage == null ||
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700191 (newPackage.equals(mPackage) && !newPackage.equals(VpnConfig.LEGACY_VPN))) {
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700192 return true;
193 }
194
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700195 // Check if the caller is authorized.
196 enforceControlPermission();
Chia-chi Yehe9107902011-07-02 01:48:50 -0700197
Chia-chi Yehe9107902011-07-02 01:48:50 -0700198 // Reset the interface and hide the notification.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700199 if (mInterface != null) {
200 jniReset(mInterface);
Jeff Sharkey899223b2012-08-04 15:24:58 -0700201 final long token = Binder.clearCallingIdentity();
202 try {
203 mCallback.restore();
204 hideNotification();
205 } finally {
206 Binder.restoreCallingIdentity(token);
207 }
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700208 mInterface = null;
Chia-chi Yehe9107902011-07-02 01:48:50 -0700209 }
210
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700211 // Revoke the connection or stop LegacyVpnRunner.
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700212 if (mConnection != null) {
213 try {
214 mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
215 Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
216 } catch (Exception e) {
217 // ignore
218 }
219 mContext.unbindService(mConnection);
220 mConnection = null;
Chia-chi Yehe9107902011-07-02 01:48:50 -0700221 } else if (mLegacyVpnRunner != null) {
222 mLegacyVpnRunner.exit();
223 mLegacyVpnRunner = null;
224 }
225
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700226 Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
227 mPackage = newPackage;
Jeff Sharkey899223b2012-08-04 15:24:58 -0700228 updateState(DetailedState.IDLE, "prepare");
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700229 return true;
Chia-chi Yehe9107902011-07-02 01:48:50 -0700230 }
231
232 /**
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700233 * Protect a socket from routing changes by binding it to the given
234 * interface. The socket is NOT closed by this method.
235 *
236 * @param socket The socket to be bound.
Jeff Sharkey899223b2012-08-04 15:24:58 -0700237 * @param interfaze The name of the interface.
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700238 */
239 public void protect(ParcelFileDescriptor socket, String interfaze) throws Exception {
240 PackageManager pm = mContext.getPackageManager();
241 ApplicationInfo app = pm.getApplicationInfo(mPackage, 0);
242 if (Binder.getCallingUid() != app.uid) {
243 throw new SecurityException("Unauthorized Caller");
244 }
245 jniProtect(socket.getFd(), interfaze);
246 }
247
248 /**
Chia-chi Yehe9107902011-07-02 01:48:50 -0700249 * Establish a VPN network and return the file descriptor of the VPN
250 * interface. This methods returns {@code null} if the application is
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700251 * revoked or not prepared.
Chia-chi Yehe9107902011-07-02 01:48:50 -0700252 *
253 * @param config The parameters to configure the network.
254 * @return The file descriptor of the VPN interface.
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700255 */
Chia-chi Yeh04ba25c2011-06-15 17:07:27 -0700256 public synchronized ParcelFileDescriptor establish(VpnConfig config) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700257 // Check if the caller is already prepared.
258 PackageManager pm = mContext.getPackageManager();
259 ApplicationInfo app = null;
260 try {
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700261 app = pm.getApplicationInfo(mPackage, 0);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700262 } catch (Exception e) {
Chia-chi Yeh7b0b8342011-06-17 14:34:11 -0700263 return null;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700264 }
265 if (Binder.getCallingUid() != app.uid) {
Chia-chi Yeh7b0b8342011-06-17 14:34:11 -0700266 return null;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700267 }
268
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700269 // Check if the service is properly declared.
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700270 Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
271 intent.setClassName(mPackage, config.user);
272 ResolveInfo info = pm.resolveService(intent, 0);
273 if (info == null) {
274 throw new SecurityException("Cannot find " + config.user);
275 }
276 if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
277 throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
278 }
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700279
Chia-chi Yeha4b87b52011-06-30 23:21:55 -0700280 // Load the label.
281 String label = app.loadLabel(pm).toString();
282
283 // Load the icon and convert it into a bitmap.
284 Drawable icon = app.loadIcon(pm);
285 Bitmap bitmap = null;
286 if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
287 int width = mContext.getResources().getDimensionPixelSize(
288 android.R.dimen.notification_large_icon_width);
289 int height = mContext.getResources().getDimensionPixelSize(
290 android.R.dimen.notification_large_icon_height);
291 icon.setBounds(0, 0, width, height);
292 bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Dianne Hackborn6311d0a2011-08-02 16:37:58 -0700293 Canvas c = new Canvas(bitmap);
294 icon.draw(c);
295 c.setBitmap(null);
Chia-chi Yeha4b87b52011-06-30 23:21:55 -0700296 }
297
Chia-chi Yehe9107902011-07-02 01:48:50 -0700298 // Configure the interface. Abort if any of these steps fails.
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700299 ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700300 try {
Jeff Sharkey899223b2012-08-04 15:24:58 -0700301 updateState(DetailedState.CONNECTING, "establish");
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700302 String interfaze = jniGetName(tun.getFd());
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700303 if (jniSetAddresses(interfaze, config.addresses) < 1) {
304 throw new IllegalArgumentException("At least one address must be specified");
305 }
306 if (config.routes != null) {
307 jniSetRoutes(interfaze, config.routes);
308 }
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700309 Connection connection = new Connection();
310 if (!mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
311 throw new IllegalStateException("Cannot bind " + config.user);
312 }
313 if (mConnection != null) {
314 mContext.unbindService(mConnection);
315 }
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700316 if (mInterface != null && !mInterface.equals(interfaze)) {
317 jniReset(mInterface);
Chia-chi Yehf4e3bf82011-06-30 12:33:17 -0700318 }
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700319 mConnection = connection;
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700320 mInterface = interfaze;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700321 } catch (RuntimeException e) {
Jeff Sharkey899223b2012-08-04 15:24:58 -0700322 updateState(DetailedState.FAILED, "establish");
Jeff Sharkey065b2992012-08-05 14:16:48 -0700323 IoUtils.closeQuietly(tun);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700324 throw e;
325 }
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700326 Log.i(TAG, "Established by " + config.user + " on " + mInterface);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700327
Chia-chi Yeh8909b102011-07-01 01:09:42 -0700328 // Fill more values.
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700329 config.user = mPackage;
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700330 config.interfaze = mInterface;
Chia-chi Yeh8909b102011-07-01 01:09:42 -0700331
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700332 // Override DNS servers and show the notification.
Jeff Sharkey899223b2012-08-04 15:24:58 -0700333 final long token = Binder.clearCallingIdentity();
334 try {
335 mCallback.override(config.dnsServers, config.searchDomains);
336 showNotification(config, label, bitmap);
337 } finally {
338 Binder.restoreCallingIdentity(token);
339 }
340 // TODO: ensure that contract class eventually marks as connected
341 updateState(DetailedState.AUTHENTICATING, "establish");
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700342 return tun;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700343 }
344
Jeff Sharkey899223b2012-08-04 15:24:58 -0700345 @Deprecated
346 public synchronized void interfaceStatusChanged(String iface, boolean up) {
347 try {
348 mObserver.interfaceStatusChanged(iface, up);
349 } catch (RemoteException e) {
350 // ignored; target is local
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700351 }
352 }
353
Jeff Sharkey899223b2012-08-04 15:24:58 -0700354 private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
355 @Override
356 public void interfaceStatusChanged(String interfaze, boolean up) {
357 synchronized (Vpn.this) {
358 if (!up && mLegacyVpnRunner != null) {
359 mLegacyVpnRunner.check(interfaze);
360 }
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700361 }
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700362 }
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700363
Jeff Sharkey899223b2012-08-04 15:24:58 -0700364 @Override
365 public void interfaceRemoved(String interfaze) {
366 synchronized (Vpn.this) {
367 if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
368 final long token = Binder.clearCallingIdentity();
369 try {
370 mCallback.restore();
371 hideNotification();
372 } finally {
373 Binder.restoreCallingIdentity(token);
374 }
375 mInterface = null;
376 if (mConnection != null) {
377 mContext.unbindService(mConnection);
378 mConnection = null;
379 updateState(DetailedState.DISCONNECTED, "interfaceRemoved");
380 } else if (mLegacyVpnRunner != null) {
381 mLegacyVpnRunner.exit();
382 mLegacyVpnRunner = null;
383 }
384 }
385 }
386 }
387 };
Haoyu Baidb3c8672012-06-20 14:29:57 -0700388
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700389 private void enforceControlPermission() {
390 // System user is allowed to control VPN.
391 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
392 return;
393 }
394
395 try {
396 // System dialogs are also allowed to control VPN.
397 PackageManager pm = mContext.getPackageManager();
398 ApplicationInfo app = pm.getApplicationInfo(VpnConfig.DIALOGS_PACKAGE, 0);
399 if (Binder.getCallingUid() == app.uid) {
400 return;
401 }
402 } catch (Exception e) {
403 // ignore
404 }
405
406 throw new SecurityException("Unauthorized Caller");
407 }
408
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700409 private class Connection implements ServiceConnection {
410 private IBinder mService;
411
412 @Override
413 public void onServiceConnected(ComponentName name, IBinder service) {
414 mService = service;
415 }
416
417 @Override
418 public void onServiceDisconnected(ComponentName name) {
419 mService = null;
420 }
421 }
422
Chia-chi Yeha4b87b52011-06-30 23:21:55 -0700423 private void showNotification(VpnConfig config, String label, Bitmap icon) {
Jeff Sharkey69ddab42012-08-25 00:05:46 -0700424 if (!mEnableNotif) return;
Jeff Sharkey899223b2012-08-04 15:24:58 -0700425 mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext, config);
426
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700427 NotificationManager nm = (NotificationManager)
428 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
429
430 if (nm != null) {
Chia-chi Yeha4b87b52011-06-30 23:21:55 -0700431 String title = (label == null) ? mContext.getString(R.string.vpn_title) :
432 mContext.getString(R.string.vpn_title_long, label);
Chia-chi Yeh34e78132011-07-03 03:07:07 -0700433 String text = (config.session == null) ? mContext.getString(R.string.vpn_text) :
434 mContext.getString(R.string.vpn_text_long, config.session);
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700435 config.startTime = SystemClock.elapsedRealtime();
Chia-chi Yeha4b87b52011-06-30 23:21:55 -0700436
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700437 Notification notification = new Notification.Builder(mContext)
438 .setSmallIcon(R.drawable.vpn_connected)
Chia-chi Yeha4b87b52011-06-30 23:21:55 -0700439 .setLargeIcon(icon)
440 .setContentTitle(title)
Chia-chi Yehf8905fd2011-06-14 16:35:02 -0700441 .setContentText(text)
Jeff Sharkey899223b2012-08-04 15:24:58 -0700442 .setContentIntent(mStatusIntent)
Chia-chi Yeh50fe7092012-01-11 14:26:24 -0800443 .setDefaults(0)
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700444 .setOngoing(true)
Jeff Sharkey899223b2012-08-04 15:24:58 -0700445 .build();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700446 nm.notifyAsUser(null, R.drawable.vpn_connected, notification, UserHandle.ALL);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700447 }
448 }
449
450 private void hideNotification() {
Jeff Sharkey69ddab42012-08-25 00:05:46 -0700451 if (!mEnableNotif) return;
Jeff Sharkey899223b2012-08-04 15:24:58 -0700452 mStatusIntent = null;
453
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700454 NotificationManager nm = (NotificationManager)
455 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
456
457 if (nm != null) {
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700458 nm.cancelAsUser(null, R.drawable.vpn_connected, UserHandle.ALL);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700459 }
460 }
461
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700462 private native int jniCreate(int mtu);
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700463 private native String jniGetName(int tun);
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700464 private native int jniSetAddresses(String interfaze, String addresses);
465 private native int jniSetRoutes(String interfaze, String routes);
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700466 private native void jniReset(String interfaze);
467 private native int jniCheck(String interfaze);
468 private native void jniProtect(int socket, String interfaze);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700469
Jeff Sharkey82f85212012-08-24 11:17:25 -0700470 private static String findLegacyVpnGateway(LinkProperties prop) {
471 for (RouteInfo route : prop.getRoutes()) {
472 // Currently legacy VPN only works on IPv4.
473 if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
474 return route.getGateway().getHostAddress();
475 }
476 }
Jeff Sharkey899223b2012-08-04 15:24:58 -0700477
Jeff Sharkey82f85212012-08-24 11:17:25 -0700478 throw new IllegalStateException("Unable to find suitable gateway");
479 }
480
481 /**
482 * Start legacy VPN, controlling native daemons as needed. Creates a
483 * secondary thread to perform connection work, returning quickly.
484 */
485 public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
Robert Greenwalt5a6bdc42013-02-15 10:56:35 -0800486 enforceControlPermission();
Kenny Rootb9594ce2013-02-14 10:18:38 -0800487 if (!keyStore.isUnlocked()) {
Jeff Sharkey82f85212012-08-24 11:17:25 -0700488 throw new IllegalStateException("KeyStore isn't unlocked");
489 }
490
491 final String iface = egress.getInterfaceName();
492 final String gateway = findLegacyVpnGateway(egress);
493
494 // Load certificates.
495 String privateKey = "";
496 String userCert = "";
497 String caCert = "";
498 String serverCert = "";
499 if (!profile.ipsecUserCert.isEmpty()) {
500 privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
501 byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
Elliott Hughesd396a442013-06-28 16:24:48 -0700502 userCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -0700503 }
504 if (!profile.ipsecCaCert.isEmpty()) {
505 byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
Elliott Hughesd396a442013-06-28 16:24:48 -0700506 caCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -0700507 }
508 if (!profile.ipsecServerCert.isEmpty()) {
509 byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
Elliott Hughesd396a442013-06-28 16:24:48 -0700510 serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -0700511 }
512 if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
513 throw new IllegalStateException("Cannot load credentials");
514 }
515
516 // Prepare arguments for racoon.
517 String[] racoon = null;
518 switch (profile.type) {
519 case VpnProfile.TYPE_L2TP_IPSEC_PSK:
520 racoon = new String[] {
521 iface, profile.server, "udppsk", profile.ipsecIdentifier,
522 profile.ipsecSecret, "1701",
523 };
524 break;
525 case VpnProfile.TYPE_L2TP_IPSEC_RSA:
526 racoon = new String[] {
527 iface, profile.server, "udprsa", privateKey, userCert,
528 caCert, serverCert, "1701",
529 };
530 break;
531 case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
532 racoon = new String[] {
533 iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
534 profile.ipsecSecret, profile.username, profile.password, "", gateway,
535 };
536 break;
537 case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
538 racoon = new String[] {
539 iface, profile.server, "xauthrsa", privateKey, userCert,
540 caCert, serverCert, profile.username, profile.password, "", gateway,
541 };
542 break;
543 case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
544 racoon = new String[] {
545 iface, profile.server, "hybridrsa",
546 caCert, serverCert, profile.username, profile.password, "", gateway,
547 };
548 break;
549 }
550
551 // Prepare arguments for mtpd.
552 String[] mtpd = null;
553 switch (profile.type) {
554 case VpnProfile.TYPE_PPTP:
555 mtpd = new String[] {
556 iface, "pptp", profile.server, "1723",
557 "name", profile.username, "password", profile.password,
558 "linkname", "vpn", "refuse-eap", "nodefaultroute",
559 "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
560 (profile.mppe ? "+mppe" : "nomppe"),
561 };
562 break;
563 case VpnProfile.TYPE_L2TP_IPSEC_PSK:
564 case VpnProfile.TYPE_L2TP_IPSEC_RSA:
565 mtpd = new String[] {
566 iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
567 "name", profile.username, "password", profile.password,
568 "linkname", "vpn", "refuse-eap", "nodefaultroute",
569 "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
570 };
571 break;
572 }
573
574 VpnConfig config = new VpnConfig();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700575 config.legacy = true;
Jeff Sharkey82f85212012-08-24 11:17:25 -0700576 config.user = profile.key;
577 config.interfaze = iface;
578 config.session = profile.name;
579 config.routes = profile.routes;
580 if (!profile.dnsServers.isEmpty()) {
581 config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
582 }
583 if (!profile.searchDomains.isEmpty()) {
584 config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
585 }
Jeff Sharkey82f85212012-08-24 11:17:25 -0700586 startLegacyVpn(config, racoon, mtpd);
587 }
588
589 private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
590 stopLegacyVpn();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700591
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700592 // Prepare for the new request. This also checks the caller.
593 prepare(null, VpnConfig.LEGACY_VPN);
Jeff Sharkey899223b2012-08-04 15:24:58 -0700594 updateState(DetailedState.CONNECTING, "startLegacyVpn");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700595
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700596 // Start a new LegacyVpnRunner and we are done!
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700597 mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
598 mLegacyVpnRunner.start();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700599 }
600
Jeff Sharkey899223b2012-08-04 15:24:58 -0700601 public synchronized void stopLegacyVpn() {
602 if (mLegacyVpnRunner != null) {
603 mLegacyVpnRunner.exit();
604 mLegacyVpnRunner = null;
605
606 synchronized (LegacyVpnRunner.TAG) {
607 // wait for old thread to completely finish before spinning up
608 // new instance, otherwise state updates can be out of order.
609 }
610 }
611 }
612
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700613 /**
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700614 * Return the information of the current ongoing legacy VPN.
615 */
616 public synchronized LegacyVpnInfo getLegacyVpnInfo() {
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700617 // Check if the caller is authorized.
618 enforceControlPermission();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700619 if (mLegacyVpnRunner == null) return null;
620
621 final LegacyVpnInfo info = new LegacyVpnInfo();
622 info.key = mLegacyVpnRunner.mConfig.user;
623 info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
624 if (mNetworkInfo.isConnected()) {
625 info.intent = mStatusIntent;
626 }
627 return info;
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700628 }
629
Jeff Sharkey69ddab42012-08-25 00:05:46 -0700630 public VpnConfig getLegacyVpnConfig() {
631 if (mLegacyVpnRunner != null) {
632 return mLegacyVpnRunner.mConfig;
633 } else {
634 return null;
635 }
636 }
637
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700638 /**
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700639 * Bringing up a VPN connection takes time, and that is all this thread
640 * does. Here we have plenty of time. The only thing we need to take
641 * care of is responding to interruptions as soon as possible. Otherwise
642 * requests will be piled up. This can be done in a Handler as a state
643 * machine, but it is much easier to read in the current form.
644 */
645 private class LegacyVpnRunner extends Thread {
646 private static final String TAG = "LegacyVpnRunner";
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700647
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700648 private final VpnConfig mConfig;
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700649 private final String[] mDaemons;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700650 private final String[][] mArguments;
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700651 private final LocalSocket[] mSockets;
Robert Greenwalt53c04bd2012-10-12 17:02:45 -0700652 private final String mOuterInterface;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700653 private final AtomicInteger mOuterConnection =
654 new AtomicInteger(ConnectivityManager.TYPE_NONE);
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700655
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700656 private long mTimer = -1;
657
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700658 /**
659 * Watch for the outer connection (passing in the constructor) going away.
660 */
661 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
662 @Override
663 public void onReceive(Context context, Intent intent) {
Jeff Sharkey57666932013-04-30 17:01:57 -0700664 if (!mEnableTeardown) return;
665
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700666 if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
667 if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
668 ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
669 NetworkInfo info = (NetworkInfo)intent.getExtra(
670 ConnectivityManager.EXTRA_NETWORK_INFO);
671 if (info != null && !info.isConnectedOrConnecting()) {
672 try {
673 mObserver.interfaceStatusChanged(mOuterInterface, false);
674 } catch (RemoteException e) {}
675 }
676 }
677 }
678 }
679 };
680
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700681 public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700682 super(TAG);
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700683 mConfig = config;
684 mDaemons = new String[] {"racoon", "mtpd"};
Jeff Sharkey899223b2012-08-04 15:24:58 -0700685 // TODO: clear arguments from memory once launched
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700686 mArguments = new String[][] {racoon, mtpd};
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700687 mSockets = new LocalSocket[mDaemons.length];
Robert Greenwalt53c04bd2012-10-12 17:02:45 -0700688
689 // This is the interface which VPN is running on,
690 // mConfig.interfaze will change to point to OUR
691 // internal interface soon. TODO - add inner/outer to mconfig
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700692 // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
693 // we will leave the VPN up. We should check that it's still there/connected after
694 // registering
Robert Greenwalt53c04bd2012-10-12 17:02:45 -0700695 mOuterInterface = mConfig.interfaze;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700696
697 try {
698 mOuterConnection.set(
699 mConnService.findConnectionTypeForIface(mOuterInterface));
700 } catch (Exception e) {
701 mOuterConnection.set(ConnectivityManager.TYPE_NONE);
702 }
703
704 IntentFilter filter = new IntentFilter();
705 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
706 mContext.registerReceiver(mBroadcastReceiver, filter);
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700707 }
708
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700709 public void check(String interfaze) {
Robert Greenwalt53c04bd2012-10-12 17:02:45 -0700710 if (interfaze.equals(mOuterInterface)) {
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700711 Log.i(TAG, "Legacy VPN is going down with " + interfaze);
712 exit();
713 }
714 }
715
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700716 public void exit() {
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700717 // We assume that everything is reset after stopping the daemons.
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700718 interrupt();
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700719 for (LocalSocket socket : mSockets) {
Jeff Sharkey065b2992012-08-05 14:16:48 -0700720 IoUtils.closeQuietly(socket);
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700721 }
Jeff Sharkey899223b2012-08-04 15:24:58 -0700722 updateState(DetailedState.DISCONNECTED, "exit");
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700723 try {
724 mContext.unregisterReceiver(mBroadcastReceiver);
725 } catch (IllegalArgumentException e) {}
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700726 }
727
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700728 @Override
729 public void run() {
730 // Wait for the previous thread since it has been interrupted.
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700731 Log.v(TAG, "Waiting");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700732 synchronized (TAG) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700733 Log.v(TAG, "Executing");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700734 execute();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700735 monitorDaemons();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700736 }
737 }
738
739 private void checkpoint(boolean yield) throws InterruptedException {
740 long now = SystemClock.elapsedRealtime();
741 if (mTimer == -1) {
742 mTimer = now;
743 Thread.sleep(1);
Chia-chi Yeh7ef86112011-07-22 15:46:52 -0700744 } else if (now - mTimer <= 60000) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700745 Thread.sleep(yield ? 200 : 1);
746 } else {
Jeff Sharkey899223b2012-08-04 15:24:58 -0700747 updateState(DetailedState.FAILED, "checkpoint");
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700748 throw new IllegalStateException("Time is up");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700749 }
750 }
751
752 private void execute() {
753 // Catch all exceptions so we can clean up few things.
Jeff Sharkey899223b2012-08-04 15:24:58 -0700754 boolean initFinished = false;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700755 try {
756 // Initialize the timer.
757 checkpoint(false);
758
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700759 // Wait for the daemons to stop.
760 for (String daemon : mDaemons) {
Jeff Sharkey088f29f2012-08-05 14:55:04 -0700761 while (!SystemService.isStopped(daemon)) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700762 checkpoint(true);
763 }
764 }
765
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700766 // Clear the previous state.
767 File state = new File("/data/misc/vpn/state");
768 state.delete();
769 if (state.exists()) {
770 throw new IllegalStateException("Cannot delete the state");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700771 }
Chia-chi Yehc1872732011-12-08 16:51:41 -0800772 new File("/data/misc/vpn/abort").delete();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700773 initFinished = true;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700774
Chia-chi Yehe9107902011-07-02 01:48:50 -0700775 // Check if we need to restart any of the daemons.
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700776 boolean restart = false;
777 for (String[] arguments : mArguments) {
778 restart = restart || (arguments != null);
779 }
780 if (!restart) {
Jeff Sharkey899223b2012-08-04 15:24:58 -0700781 updateState(DetailedState.DISCONNECTED, "execute");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700782 return;
783 }
Jeff Sharkey899223b2012-08-04 15:24:58 -0700784 updateState(DetailedState.CONNECTING, "execute");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700785
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700786 // Start the daemon with arguments.
787 for (int i = 0; i < mDaemons.length; ++i) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700788 String[] arguments = mArguments[i];
789 if (arguments == null) {
790 continue;
791 }
792
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700793 // Start the daemon.
794 String daemon = mDaemons[i];
Jeff Sharkey088f29f2012-08-05 14:55:04 -0700795 SystemService.start(daemon);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700796
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700797 // Wait for the daemon to start.
Jeff Sharkey088f29f2012-08-05 14:55:04 -0700798 while (!SystemService.isRunning(daemon)) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700799 checkpoint(true);
800 }
801
802 // Create the control socket.
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700803 mSockets[i] = new LocalSocket();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700804 LocalSocketAddress address = new LocalSocketAddress(
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700805 daemon, LocalSocketAddress.Namespace.RESERVED);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700806
807 // Wait for the socket to connect.
808 while (true) {
809 try {
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700810 mSockets[i].connect(address);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700811 break;
812 } catch (Exception e) {
813 // ignore
814 }
815 checkpoint(true);
816 }
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700817 mSockets[i].setSoTimeout(500);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700818
819 // Send over the arguments.
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700820 OutputStream out = mSockets[i].getOutputStream();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700821 for (String argument : arguments) {
Elliott Hughesd396a442013-06-28 16:24:48 -0700822 byte[] bytes = argument.getBytes(StandardCharsets.UTF_8);
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700823 if (bytes.length >= 0xFFFF) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700824 throw new IllegalArgumentException("Argument is too large");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700825 }
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700826 out.write(bytes.length >> 8);
827 out.write(bytes.length);
828 out.write(bytes);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700829 checkpoint(false);
830 }
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700831 out.write(0xFF);
832 out.write(0xFF);
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700833 out.flush();
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700834
835 // Wait for End-of-File.
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700836 InputStream in = mSockets[i].getInputStream();
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700837 while (true) {
838 try {
839 if (in.read() == -1) {
840 break;
841 }
842 } catch (Exception e) {
843 // ignore
844 }
845 checkpoint(true);
846 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700847 }
848
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700849 // Wait for the daemons to create the new state.
850 while (!state.exists()) {
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700851 // Check if a running daemon is dead.
852 for (int i = 0; i < mDaemons.length; ++i) {
853 String daemon = mDaemons[i];
Jeff Sharkey088f29f2012-08-05 14:55:04 -0700854 if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700855 throw new IllegalStateException(daemon + " is dead");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700856 }
857 }
858 checkpoint(true);
859 }
860
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700861 // Now we are connected. Read and parse the new state.
Chia-chi Yehc1bac3a2011-12-16 15:00:31 -0800862 String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700863 if (parameters.length != 6) {
864 throw new IllegalStateException("Cannot parse the state");
865 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700866
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700867 // Set the interface and the addresses in the config.
868 mConfig.interfaze = parameters[0].trim();
869 mConfig.addresses = parameters[1].trim();
870
871 // Set the routes if they are not set in the config.
872 if (mConfig.routes == null || mConfig.routes.isEmpty()) {
873 mConfig.routes = parameters[2].trim();
874 }
875
876 // Set the DNS servers if they are not set in the config.
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700877 if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700878 String dnsServers = parameters[3].trim();
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700879 if (!dnsServers.isEmpty()) {
880 mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
881 }
882 }
883
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700884 // Set the search domains if they are not set in the config.
885 if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
886 String searchDomains = parameters[4].trim();
887 if (!searchDomains.isEmpty()) {
888 mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
889 }
890 }
Chia-chi Yehe9107902011-07-02 01:48:50 -0700891
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700892 // Set the routes.
893 jniSetRoutes(mConfig.interfaze, mConfig.routes);
894
895 // Here is the last step and it must be done synchronously.
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700896 synchronized (Vpn.this) {
897 // Check if the thread is interrupted while we are waiting.
898 checkpoint(false);
899
Chia-chi Yehe9107902011-07-02 01:48:50 -0700900 // Check if the interface is gone while we are waiting.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700901 if (jniCheck(mConfig.interfaze) == 0) {
Chia-chi Yeh34e78132011-07-03 03:07:07 -0700902 throw new IllegalStateException(mConfig.interfaze + " is gone");
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700903 }
Chia-chi Yehe9107902011-07-02 01:48:50 -0700904
905 // Now INetworkManagementEventObserver is watching our back.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700906 mInterface = mConfig.interfaze;
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700907 mCallback.override(mConfig.dnsServers, mConfig.searchDomains);
908 showNotification(mConfig, null, null);
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700909
910 Log.i(TAG, "Connected!");
Jeff Sharkey899223b2012-08-04 15:24:58 -0700911 updateState(DetailedState.CONNECTED, "execute");
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700912 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700913 } catch (Exception e) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700914 Log.i(TAG, "Aborting", e);
Chia-chi Yehe9107902011-07-02 01:48:50 -0700915 exit();
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700916 } finally {
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700917 // Kill the daemons if they fail to stop.
Jeff Sharkey899223b2012-08-04 15:24:58 -0700918 if (!initFinished) {
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700919 for (String daemon : mDaemons) {
Jeff Sharkey088f29f2012-08-05 14:55:04 -0700920 SystemService.stop(daemon);
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700921 }
922 }
923
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700924 // Do not leave an unstable state.
Jeff Sharkey899223b2012-08-04 15:24:58 -0700925 if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
926 updateState(DetailedState.FAILED, "execute");
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700927 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700928 }
929 }
Jeff Sharkey899223b2012-08-04 15:24:58 -0700930
931 /**
932 * Monitor the daemons we started, moving to disconnected state if the
933 * underlying services fail.
934 */
935 private void monitorDaemons() {
936 if (!mNetworkInfo.isConnected()) {
937 return;
938 }
939
940 try {
941 while (true) {
942 Thread.sleep(2000);
943 for (int i = 0; i < mDaemons.length; i++) {
944 if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
945 return;
946 }
947 }
948 }
949 } catch (InterruptedException e) {
950 Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
951 } finally {
952 for (String daemon : mDaemons) {
953 SystemService.stop(daemon);
954 }
955
956 updateState(DetailedState.DISCONNECTED, "babysit");
957 }
958 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700959 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700960}