blob: 63d39588bb5d53f906db67925f91ae4f1b9431fe [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
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +0900470 private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
471 for (RouteInfo route : prop.getAllRoutes()) {
Jeff Sharkey82f85212012-08-24 11:17:25 -0700472 // Currently legacy VPN only works on IPv4.
473 if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +0900474 return route;
Jeff Sharkey82f85212012-08-24 11:17:25 -0700475 }
476 }
Jeff Sharkey899223b2012-08-04 15:24:58 -0700477
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +0900478 throw new IllegalStateException("Unable to find IPv4 default gateway");
Jeff Sharkey82f85212012-08-24 11:17:25 -0700479 }
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
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +0900491 final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
492 final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
493 final String iface = ipv4DefaultRoute.getInterface();
Jeff Sharkey82f85212012-08-24 11:17:25 -0700494
495 // Load certificates.
496 String privateKey = "";
497 String userCert = "";
498 String caCert = "";
499 String serverCert = "";
500 if (!profile.ipsecUserCert.isEmpty()) {
501 privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
502 byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
Elliott Hughesd396a442013-06-28 16:24:48 -0700503 userCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -0700504 }
505 if (!profile.ipsecCaCert.isEmpty()) {
506 byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
Elliott Hughesd396a442013-06-28 16:24:48 -0700507 caCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -0700508 }
509 if (!profile.ipsecServerCert.isEmpty()) {
510 byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
Elliott Hughesd396a442013-06-28 16:24:48 -0700511 serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -0700512 }
513 if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
514 throw new IllegalStateException("Cannot load credentials");
515 }
516
517 // Prepare arguments for racoon.
518 String[] racoon = null;
519 switch (profile.type) {
520 case VpnProfile.TYPE_L2TP_IPSEC_PSK:
521 racoon = new String[] {
522 iface, profile.server, "udppsk", profile.ipsecIdentifier,
523 profile.ipsecSecret, "1701",
524 };
525 break;
526 case VpnProfile.TYPE_L2TP_IPSEC_RSA:
527 racoon = new String[] {
528 iface, profile.server, "udprsa", privateKey, userCert,
529 caCert, serverCert, "1701",
530 };
531 break;
532 case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
533 racoon = new String[] {
534 iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
535 profile.ipsecSecret, profile.username, profile.password, "", gateway,
536 };
537 break;
538 case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
539 racoon = new String[] {
540 iface, profile.server, "xauthrsa", privateKey, userCert,
541 caCert, serverCert, profile.username, profile.password, "", gateway,
542 };
543 break;
544 case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
545 racoon = new String[] {
546 iface, profile.server, "hybridrsa",
547 caCert, serverCert, profile.username, profile.password, "", gateway,
548 };
549 break;
550 }
551
552 // Prepare arguments for mtpd.
553 String[] mtpd = null;
554 switch (profile.type) {
555 case VpnProfile.TYPE_PPTP:
556 mtpd = new String[] {
557 iface, "pptp", profile.server, "1723",
558 "name", profile.username, "password", profile.password,
559 "linkname", "vpn", "refuse-eap", "nodefaultroute",
560 "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
561 (profile.mppe ? "+mppe" : "nomppe"),
562 };
563 break;
564 case VpnProfile.TYPE_L2TP_IPSEC_PSK:
565 case VpnProfile.TYPE_L2TP_IPSEC_RSA:
566 mtpd = new String[] {
567 iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
568 "name", profile.username, "password", profile.password,
569 "linkname", "vpn", "refuse-eap", "nodefaultroute",
570 "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
571 };
572 break;
573 }
574
575 VpnConfig config = new VpnConfig();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700576 config.legacy = true;
Jeff Sharkey82f85212012-08-24 11:17:25 -0700577 config.user = profile.key;
578 config.interfaze = iface;
579 config.session = profile.name;
580 config.routes = profile.routes;
581 if (!profile.dnsServers.isEmpty()) {
582 config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
583 }
584 if (!profile.searchDomains.isEmpty()) {
585 config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
586 }
Jeff Sharkey82f85212012-08-24 11:17:25 -0700587 startLegacyVpn(config, racoon, mtpd);
588 }
589
590 private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
591 stopLegacyVpn();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700592
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700593 // Prepare for the new request. This also checks the caller.
594 prepare(null, VpnConfig.LEGACY_VPN);
Jeff Sharkey899223b2012-08-04 15:24:58 -0700595 updateState(DetailedState.CONNECTING, "startLegacyVpn");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700596
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700597 // Start a new LegacyVpnRunner and we are done!
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700598 mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
599 mLegacyVpnRunner.start();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700600 }
601
Jeff Sharkey899223b2012-08-04 15:24:58 -0700602 public synchronized void stopLegacyVpn() {
603 if (mLegacyVpnRunner != null) {
604 mLegacyVpnRunner.exit();
605 mLegacyVpnRunner = null;
606
607 synchronized (LegacyVpnRunner.TAG) {
608 // wait for old thread to completely finish before spinning up
609 // new instance, otherwise state updates can be out of order.
610 }
611 }
612 }
613
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700614 /**
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700615 * Return the information of the current ongoing legacy VPN.
616 */
617 public synchronized LegacyVpnInfo getLegacyVpnInfo() {
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700618 // Check if the caller is authorized.
619 enforceControlPermission();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700620 if (mLegacyVpnRunner == null) return null;
621
622 final LegacyVpnInfo info = new LegacyVpnInfo();
623 info.key = mLegacyVpnRunner.mConfig.user;
624 info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
625 if (mNetworkInfo.isConnected()) {
626 info.intent = mStatusIntent;
627 }
628 return info;
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700629 }
630
Jeff Sharkey69ddab42012-08-25 00:05:46 -0700631 public VpnConfig getLegacyVpnConfig() {
632 if (mLegacyVpnRunner != null) {
633 return mLegacyVpnRunner.mConfig;
634 } else {
635 return null;
636 }
637 }
638
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700639 /**
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700640 * Bringing up a VPN connection takes time, and that is all this thread
641 * does. Here we have plenty of time. The only thing we need to take
642 * care of is responding to interruptions as soon as possible. Otherwise
643 * requests will be piled up. This can be done in a Handler as a state
644 * machine, but it is much easier to read in the current form.
645 */
646 private class LegacyVpnRunner extends Thread {
647 private static final String TAG = "LegacyVpnRunner";
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700648
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700649 private final VpnConfig mConfig;
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700650 private final String[] mDaemons;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700651 private final String[][] mArguments;
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700652 private final LocalSocket[] mSockets;
Robert Greenwalt53c04bd2012-10-12 17:02:45 -0700653 private final String mOuterInterface;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700654 private final AtomicInteger mOuterConnection =
655 new AtomicInteger(ConnectivityManager.TYPE_NONE);
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700656
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700657 private long mTimer = -1;
658
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700659 /**
660 * Watch for the outer connection (passing in the constructor) going away.
661 */
662 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
663 @Override
664 public void onReceive(Context context, Intent intent) {
Jeff Sharkey57666932013-04-30 17:01:57 -0700665 if (!mEnableTeardown) return;
666
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700667 if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
668 if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
669 ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
670 NetworkInfo info = (NetworkInfo)intent.getExtra(
671 ConnectivityManager.EXTRA_NETWORK_INFO);
672 if (info != null && !info.isConnectedOrConnecting()) {
673 try {
674 mObserver.interfaceStatusChanged(mOuterInterface, false);
675 } catch (RemoteException e) {}
676 }
677 }
678 }
679 }
680 };
681
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700682 public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700683 super(TAG);
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700684 mConfig = config;
685 mDaemons = new String[] {"racoon", "mtpd"};
Jeff Sharkey899223b2012-08-04 15:24:58 -0700686 // TODO: clear arguments from memory once launched
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700687 mArguments = new String[][] {racoon, mtpd};
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700688 mSockets = new LocalSocket[mDaemons.length];
Robert Greenwalt53c04bd2012-10-12 17:02:45 -0700689
690 // This is the interface which VPN is running on,
691 // mConfig.interfaze will change to point to OUR
692 // internal interface soon. TODO - add inner/outer to mconfig
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700693 // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
694 // we will leave the VPN up. We should check that it's still there/connected after
695 // registering
Robert Greenwalt53c04bd2012-10-12 17:02:45 -0700696 mOuterInterface = mConfig.interfaze;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700697
698 try {
699 mOuterConnection.set(
700 mConnService.findConnectionTypeForIface(mOuterInterface));
701 } catch (Exception e) {
702 mOuterConnection.set(ConnectivityManager.TYPE_NONE);
703 }
704
705 IntentFilter filter = new IntentFilter();
706 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
707 mContext.registerReceiver(mBroadcastReceiver, filter);
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700708 }
709
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700710 public void check(String interfaze) {
Robert Greenwalt53c04bd2012-10-12 17:02:45 -0700711 if (interfaze.equals(mOuterInterface)) {
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700712 Log.i(TAG, "Legacy VPN is going down with " + interfaze);
713 exit();
714 }
715 }
716
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700717 public void exit() {
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700718 // We assume that everything is reset after stopping the daemons.
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700719 interrupt();
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700720 for (LocalSocket socket : mSockets) {
Jeff Sharkey065b2992012-08-05 14:16:48 -0700721 IoUtils.closeQuietly(socket);
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700722 }
Jeff Sharkey899223b2012-08-04 15:24:58 -0700723 updateState(DetailedState.DISCONNECTED, "exit");
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700724 try {
725 mContext.unregisterReceiver(mBroadcastReceiver);
726 } catch (IllegalArgumentException e) {}
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700727 }
728
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700729 @Override
730 public void run() {
731 // Wait for the previous thread since it has been interrupted.
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700732 Log.v(TAG, "Waiting");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700733 synchronized (TAG) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700734 Log.v(TAG, "Executing");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700735 execute();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700736 monitorDaemons();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700737 }
738 }
739
740 private void checkpoint(boolean yield) throws InterruptedException {
741 long now = SystemClock.elapsedRealtime();
742 if (mTimer == -1) {
743 mTimer = now;
744 Thread.sleep(1);
Chia-chi Yeh7ef86112011-07-22 15:46:52 -0700745 } else if (now - mTimer <= 60000) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700746 Thread.sleep(yield ? 200 : 1);
747 } else {
Jeff Sharkey899223b2012-08-04 15:24:58 -0700748 updateState(DetailedState.FAILED, "checkpoint");
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700749 throw new IllegalStateException("Time is up");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700750 }
751 }
752
753 private void execute() {
754 // Catch all exceptions so we can clean up few things.
Jeff Sharkey899223b2012-08-04 15:24:58 -0700755 boolean initFinished = false;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700756 try {
757 // Initialize the timer.
758 checkpoint(false);
759
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700760 // Wait for the daemons to stop.
761 for (String daemon : mDaemons) {
Jeff Sharkey088f29f2012-08-05 14:55:04 -0700762 while (!SystemService.isStopped(daemon)) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700763 checkpoint(true);
764 }
765 }
766
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700767 // Clear the previous state.
768 File state = new File("/data/misc/vpn/state");
769 state.delete();
770 if (state.exists()) {
771 throw new IllegalStateException("Cannot delete the state");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700772 }
Chia-chi Yehc1872732011-12-08 16:51:41 -0800773 new File("/data/misc/vpn/abort").delete();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700774 initFinished = true;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700775
Chia-chi Yehe9107902011-07-02 01:48:50 -0700776 // Check if we need to restart any of the daemons.
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700777 boolean restart = false;
778 for (String[] arguments : mArguments) {
779 restart = restart || (arguments != null);
780 }
781 if (!restart) {
Jeff Sharkey899223b2012-08-04 15:24:58 -0700782 updateState(DetailedState.DISCONNECTED, "execute");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700783 return;
784 }
Jeff Sharkey899223b2012-08-04 15:24:58 -0700785 updateState(DetailedState.CONNECTING, "execute");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700786
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700787 // Start the daemon with arguments.
788 for (int i = 0; i < mDaemons.length; ++i) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700789 String[] arguments = mArguments[i];
790 if (arguments == null) {
791 continue;
792 }
793
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700794 // Start the daemon.
795 String daemon = mDaemons[i];
Jeff Sharkey088f29f2012-08-05 14:55:04 -0700796 SystemService.start(daemon);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700797
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700798 // Wait for the daemon to start.
Jeff Sharkey088f29f2012-08-05 14:55:04 -0700799 while (!SystemService.isRunning(daemon)) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700800 checkpoint(true);
801 }
802
803 // Create the control socket.
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700804 mSockets[i] = new LocalSocket();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700805 LocalSocketAddress address = new LocalSocketAddress(
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700806 daemon, LocalSocketAddress.Namespace.RESERVED);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700807
808 // Wait for the socket to connect.
809 while (true) {
810 try {
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700811 mSockets[i].connect(address);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700812 break;
813 } catch (Exception e) {
814 // ignore
815 }
816 checkpoint(true);
817 }
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700818 mSockets[i].setSoTimeout(500);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700819
820 // Send over the arguments.
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700821 OutputStream out = mSockets[i].getOutputStream();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700822 for (String argument : arguments) {
Elliott Hughesd396a442013-06-28 16:24:48 -0700823 byte[] bytes = argument.getBytes(StandardCharsets.UTF_8);
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700824 if (bytes.length >= 0xFFFF) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700825 throw new IllegalArgumentException("Argument is too large");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700826 }
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700827 out.write(bytes.length >> 8);
828 out.write(bytes.length);
829 out.write(bytes);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700830 checkpoint(false);
831 }
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700832 out.write(0xFF);
833 out.write(0xFF);
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700834 out.flush();
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700835
836 // Wait for End-of-File.
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700837 InputStream in = mSockets[i].getInputStream();
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700838 while (true) {
839 try {
840 if (in.read() == -1) {
841 break;
842 }
843 } catch (Exception e) {
844 // ignore
845 }
846 checkpoint(true);
847 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700848 }
849
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700850 // Wait for the daemons to create the new state.
851 while (!state.exists()) {
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700852 // Check if a running daemon is dead.
853 for (int i = 0; i < mDaemons.length; ++i) {
854 String daemon = mDaemons[i];
Jeff Sharkey088f29f2012-08-05 14:55:04 -0700855 if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700856 throw new IllegalStateException(daemon + " is dead");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700857 }
858 }
859 checkpoint(true);
860 }
861
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700862 // Now we are connected. Read and parse the new state.
Chia-chi Yehc1bac3a2011-12-16 15:00:31 -0800863 String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700864 if (parameters.length != 6) {
865 throw new IllegalStateException("Cannot parse the state");
866 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700867
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700868 // Set the interface and the addresses in the config.
869 mConfig.interfaze = parameters[0].trim();
870 mConfig.addresses = parameters[1].trim();
871
872 // Set the routes if they are not set in the config.
873 if (mConfig.routes == null || mConfig.routes.isEmpty()) {
874 mConfig.routes = parameters[2].trim();
875 }
876
877 // Set the DNS servers if they are not set in the config.
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700878 if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700879 String dnsServers = parameters[3].trim();
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700880 if (!dnsServers.isEmpty()) {
881 mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
882 }
883 }
884
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700885 // Set the search domains if they are not set in the config.
886 if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
887 String searchDomains = parameters[4].trim();
888 if (!searchDomains.isEmpty()) {
889 mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
890 }
891 }
Chia-chi Yehe9107902011-07-02 01:48:50 -0700892
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700893 // Set the routes.
894 jniSetRoutes(mConfig.interfaze, mConfig.routes);
895
896 // Here is the last step and it must be done synchronously.
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700897 synchronized (Vpn.this) {
898 // Check if the thread is interrupted while we are waiting.
899 checkpoint(false);
900
Chia-chi Yehe9107902011-07-02 01:48:50 -0700901 // Check if the interface is gone while we are waiting.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700902 if (jniCheck(mConfig.interfaze) == 0) {
Chia-chi Yeh34e78132011-07-03 03:07:07 -0700903 throw new IllegalStateException(mConfig.interfaze + " is gone");
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700904 }
Chia-chi Yehe9107902011-07-02 01:48:50 -0700905
906 // Now INetworkManagementEventObserver is watching our back.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700907 mInterface = mConfig.interfaze;
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700908 mCallback.override(mConfig.dnsServers, mConfig.searchDomains);
909 showNotification(mConfig, null, null);
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700910
911 Log.i(TAG, "Connected!");
Jeff Sharkey899223b2012-08-04 15:24:58 -0700912 updateState(DetailedState.CONNECTED, "execute");
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700913 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700914 } catch (Exception e) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700915 Log.i(TAG, "Aborting", e);
Chia-chi Yehe9107902011-07-02 01:48:50 -0700916 exit();
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700917 } finally {
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700918 // Kill the daemons if they fail to stop.
Jeff Sharkey899223b2012-08-04 15:24:58 -0700919 if (!initFinished) {
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700920 for (String daemon : mDaemons) {
Jeff Sharkey088f29f2012-08-05 14:55:04 -0700921 SystemService.stop(daemon);
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700922 }
923 }
924
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700925 // Do not leave an unstable state.
Jeff Sharkey899223b2012-08-04 15:24:58 -0700926 if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
927 updateState(DetailedState.FAILED, "execute");
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700928 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700929 }
930 }
Jeff Sharkey899223b2012-08-04 15:24:58 -0700931
932 /**
933 * Monitor the daemons we started, moving to disconnected state if the
934 * underlying services fail.
935 */
936 private void monitorDaemons() {
937 if (!mNetworkInfo.isConnected()) {
938 return;
939 }
940
941 try {
942 while (true) {
943 Thread.sleep(2000);
944 for (int i = 0; i < mDaemons.length; i++) {
945 if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
946 return;
947 }
948 }
949 }
950 } catch (InterruptedException e) {
951 Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
952 } finally {
953 for (String daemon : mDaemons) {
954 SystemService.stop(daemon);
955 }
956
957 updateState(DetailedState.DISCONNECTED, "babysit");
958 }
959 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700960 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700961}