blob: d15254b4fb0b2976faaab1e637723c3faa64f902 [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
Chad Brubaker4ca19e82013-06-14 11:16:51 -070021import android.app.AppGlobals;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070022import android.app.Notification;
23import android.app.NotificationManager;
Jeff Sharkey899223b2012-08-04 15:24:58 -070024import android.app.PendingIntent;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -070025import android.content.BroadcastReceiver;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070026import android.content.ComponentName;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070027import android.content.Context;
28import android.content.Intent;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -070029import android.content.IntentFilter;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070030import android.content.ServiceConnection;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070031import android.content.pm.ApplicationInfo;
32import android.content.pm.PackageManager;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040033import android.content.pm.PackageManager.NameNotFoundException;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070034import android.content.pm.ResolveInfo;
Chad Brubakerc2865192013-07-10 14:46:23 -070035import android.content.pm.UserInfo;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070036import android.graphics.Bitmap;
37import android.graphics.Canvas;
38import android.graphics.drawable.Drawable;
Jeff Sharkey899223b2012-08-04 15:24:58 -070039import android.net.BaseNetworkStateTracker;
40import android.net.ConnectivityManager;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -070041import android.net.IConnectivityManager;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070042import android.net.INetworkManagementEventObserver;
Chad Brubaker4ca19e82013-06-14 11:16:51 -070043import android.net.LinkAddress;
Jeff Sharkey82f85212012-08-24 11:17:25 -070044import android.net.LinkProperties;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070045import android.net.LocalSocket;
46import android.net.LocalSocketAddress;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040047import android.net.NetworkAgent;
48import android.net.NetworkCapabilities;
Jeff Sharkey899223b2012-08-04 15:24:58 -070049import android.net.NetworkInfo;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040050import android.net.NetworkInfo.DetailedState;
Chad Brubakerc0234532014-02-14 13:24:29 -080051import android.net.NetworkUtils;
Jeff Sharkey82f85212012-08-24 11:17:25 -070052import android.net.RouteInfo;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040053import android.net.UidRange;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070054import android.os.Binder;
Chia-chi Yehc1bac3a2011-12-16 15:00:31 -080055import android.os.FileUtils;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070056import android.os.IBinder;
Jeff Sharkey899223b2012-08-04 15:24:58 -070057import android.os.INetworkManagementService;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040058import android.os.Looper;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070059import android.os.Parcel;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070060import android.os.ParcelFileDescriptor;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070061import android.os.Process;
Jeff Sharkey899223b2012-08-04 15:24:58 -070062import android.os.RemoteException;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070063import android.os.SystemClock;
Jeff Sharkey088f29f2012-08-05 14:55:04 -070064import android.os.SystemService;
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -070065import android.os.UserHandle;
Chad Brubakerc2865192013-07-10 14:46:23 -070066import android.os.UserManager;
Jeff Sharkey82f85212012-08-24 11:17:25 -070067import android.security.Credentials;
68import android.security.KeyStore;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070069import android.util.Log;
Chad Brubakerc2865192013-07-10 14:46:23 -070070import android.util.SparseBooleanArray;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070071
Chad Brubakerc2865192013-07-10 14:46:23 -070072import com.android.internal.annotations.GuardedBy;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070073import com.android.internal.R;
Chia-chi Yeh2e467642011-07-04 03:23:12 -070074import com.android.internal.net.LegacyVpnInfo;
Chia-chi Yeh04ba25c2011-06-15 17:07:27 -070075import com.android.internal.net.VpnConfig;
Jeff Sharkey82f85212012-08-24 11:17:25 -070076import com.android.internal.net.VpnProfile;
Jeff Sharkey899223b2012-08-04 15:24:58 -070077import com.android.server.net.BaseNetworkObserver;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070078
Chia-chi Yeh97a61562011-07-14 15:05:05 -070079import java.io.File;
Chia-chi Yeh97a61562011-07-14 15:05:05 -070080import java.io.InputStream;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070081import java.io.OutputStream;
Chad Brubakerc0234532014-02-14 13:24:29 -080082import java.net.InetAddress;
Jeff Sharkey82f85212012-08-24 11:17:25 -070083import java.net.Inet4Address;
Elliott Hughesd396a442013-06-28 16:24:48 -070084import java.nio.charset.StandardCharsets;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040085import java.util.ArrayList;
Chia-chi Yeh41d16852011-07-01 02:12:06 -070086import java.util.Arrays;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040087import java.util.List;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -070088import java.util.concurrent.atomic.AtomicInteger;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070089
Jeff Sharkey065b2992012-08-05 14:16:48 -070090import libcore.io.IoUtils;
91
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070092/**
93 * @hide
94 */
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040095public class Vpn {
96 private static final String NETWORKTYPE = "VPN";
Jeff Sharkey899223b2012-08-04 15:24:58 -070097 private static final String TAG = "Vpn";
98 private static final boolean LOGD = true;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040099
Jeff Sharkey899223b2012-08-04 15:24:58 -0700100 // TODO: create separate trackers for each unique VPN to support
101 // automated reconnection
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700102
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400103 private Context mContext;
104 private NetworkInfo mNetworkInfo;
105 private String mPackage;
106 private int mOwnerUID;
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700107 private String mInterface;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700108 private Connection mConnection;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700109 private LegacyVpnRunner mLegacyVpnRunner;
Jeff Sharkey899223b2012-08-04 15:24:58 -0700110 private PendingIntent mStatusIntent;
Jeff Sharkey57666932013-04-30 17:01:57 -0700111 private volatile boolean mEnableNotif = true;
112 private volatile boolean mEnableTeardown = true;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700113 private final IConnectivityManager mConnService;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400114 private final INetworkManagementService mNetd;
Chad Brubakerc2865192013-07-10 14:46:23 -0700115 private VpnConfig mConfig;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400116 private NetworkAgent mNetworkAgent;
117 private final Looper mLooper;
118 private final NetworkCapabilities mNetworkCapabilities;
Chad Brubakerc2865192013-07-10 14:46:23 -0700119
120 /* list of users using this VPN. */
121 @GuardedBy("this")
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400122 private List<UidRange> mVpnUsers = null;
Chad Brubakerc2865192013-07-10 14:46:23 -0700123 private BroadcastReceiver mUserIntentReceiver = null;
124
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700125 private final int mUserId;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700126
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400127 public Vpn(Looper looper, Context context, INetworkManagementService netService,
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700128 IConnectivityManager connService, int userId) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700129 mContext = context;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400130 mNetd = netService;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700131 mConnService = connService;
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700132 mUserId = userId;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400133 mLooper = looper;
134
135 mPackage = VpnConfig.LEGACY_VPN;
136 mOwnerUID = getAppUid(mPackage);
Jeff Sharkey899223b2012-08-04 15:24:58 -0700137
138 try {
139 netService.registerObserver(mObserver);
140 } catch (RemoteException e) {
141 Log.wtf(TAG, "Problem registering observer", e);
142 }
Chad Brubakerc2865192013-07-10 14:46:23 -0700143 if (userId == UserHandle.USER_OWNER) {
144 // Owner's VPN also needs to handle restricted users
145 mUserIntentReceiver = new BroadcastReceiver() {
146 @Override
147 public void onReceive(Context context, Intent intent) {
148 final String action = intent.getAction();
149 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
150 UserHandle.USER_NULL);
151 if (userId == UserHandle.USER_NULL) return;
152
153 if (Intent.ACTION_USER_ADDED.equals(action)) {
154 onUserAdded(userId);
155 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
156 onUserRemoved(userId);
157 }
158 }
159 };
160
161 IntentFilter intentFilter = new IntentFilter();
162 intentFilter.addAction(Intent.ACTION_USER_ADDED);
163 intentFilter.addAction(Intent.ACTION_USER_REMOVED);
164 mContext.registerReceiverAsUser(
165 mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
166 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400167
168 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
169 // TODO: Copy metered attribute and bandwidths from physical transport, b/16207332
170 mNetworkCapabilities = new NetworkCapabilities();
171 mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
172 mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
Jeff Sharkey899223b2012-08-04 15:24:58 -0700173 }
174
Jeff Sharkey57666932013-04-30 17:01:57 -0700175 /**
176 * Set if this object is responsible for showing its own notifications. When
177 * {@code false}, notifications are handled externally by someone else.
178 */
Jeff Sharkey69ddab42012-08-25 00:05:46 -0700179 public void setEnableNotifications(boolean enableNotif) {
180 mEnableNotif = enableNotif;
181 }
182
Jeff Sharkey57666932013-04-30 17:01:57 -0700183 /**
184 * Set if this object is responsible for watching for {@link NetworkInfo}
185 * teardown. When {@code false}, teardown is handled externally by someone
186 * else.
187 */
188 public void setEnableTeardown(boolean enableTeardown) {
189 mEnableTeardown = enableTeardown;
190 }
191
Jeff Sharkey899223b2012-08-04 15:24:58 -0700192 /**
193 * Update current state, dispaching event to listeners.
194 */
195 private void updateState(DetailedState detailedState, String reason) {
196 if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
197 mNetworkInfo.setDetailedState(detailedState, reason, null);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400198 if (mNetworkAgent != null) {
199 mNetworkAgent.sendNetworkInfo(mNetworkInfo);
200 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700201 }
202
203 /**
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700204 * Prepare for a VPN application. This method is designed to solve
205 * race conditions. It first compares the current prepared package
206 * with {@code oldPackage}. If they are the same, the prepared
207 * package is revoked and replaced with {@code newPackage}. If
208 * {@code oldPackage} is {@code null}, the comparison is omitted.
209 * If {@code newPackage} is the same package or {@code null}, the
210 * revocation is omitted. This method returns {@code true} if the
211 * operation is succeeded.
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700212 *
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700213 * Legacy VPN is handled specially since it is not a real package.
214 * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
215 * it can be revoked by itself.
216 *
217 * @param oldPackage The package name of the old VPN application.
218 * @param newPackage The package name of the new VPN application.
219 * @return true if the operation is succeeded.
Chia-chi Yehe9107902011-07-02 01:48:50 -0700220 */
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700221 public synchronized boolean prepare(String oldPackage, String newPackage) {
222 // Return false if the package does not match.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700223 if (oldPackage != null && !oldPackage.equals(mPackage)) {
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700224 return false;
Chia-chi Yehe9107902011-07-02 01:48:50 -0700225 }
226
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700227 // Return true if we do not need to revoke.
228 if (newPackage == null ||
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700229 (newPackage.equals(mPackage) && !newPackage.equals(VpnConfig.LEGACY_VPN))) {
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700230 return true;
231 }
232
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700233 // Check if the caller is authorized.
234 enforceControlPermission();
Chia-chi Yehe9107902011-07-02 01:48:50 -0700235
Chia-chi Yehe9107902011-07-02 01:48:50 -0700236 // Reset the interface and hide the notification.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700237 if (mInterface != null) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400238 for (UidRange uidRange : mVpnUsers) {
239 hideNotification(uidRange.getStartUser());
Jeff Sharkey899223b2012-08-04 15:24:58 -0700240 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400241 agentDisconnect();
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700242 jniReset(mInterface);
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700243 mInterface = null;
Chad Brubakerc2865192013-07-10 14:46:23 -0700244 mVpnUsers = null;
Chia-chi Yehe9107902011-07-02 01:48:50 -0700245 }
246
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700247 // Revoke the connection or stop LegacyVpnRunner.
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700248 if (mConnection != null) {
249 try {
250 mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
251 Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
252 } catch (Exception e) {
253 // ignore
254 }
255 mContext.unbindService(mConnection);
256 mConnection = null;
Chia-chi Yehe9107902011-07-02 01:48:50 -0700257 } else if (mLegacyVpnRunner != null) {
258 mLegacyVpnRunner.exit();
259 mLegacyVpnRunner = null;
260 }
261
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400262 long token = Binder.clearCallingIdentity();
263 try {
264 mNetd.denyProtect(mOwnerUID);
265 } catch (Exception e) {
266 Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
267 } finally {
268 Binder.restoreCallingIdentity(token);
269 }
270
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700271 Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
272 mPackage = newPackage;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400273 mOwnerUID = getAppUid(newPackage);
274 token = Binder.clearCallingIdentity();
275 try {
276 mNetd.allowProtect(mOwnerUID);
277 } catch (Exception e) {
278 Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
279 } finally {
280 Binder.restoreCallingIdentity(token);
281 }
Chad Brubakerc2865192013-07-10 14:46:23 -0700282 mConfig = null;
Jeff Sharkey899223b2012-08-04 15:24:58 -0700283 updateState(DetailedState.IDLE, "prepare");
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700284 return true;
Chia-chi Yehe9107902011-07-02 01:48:50 -0700285 }
286
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400287 private int getAppUid(String app) {
288 if (app == VpnConfig.LEGACY_VPN) {
289 return Process.myUid();
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700290 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400291 PackageManager pm = mContext.getPackageManager();
292 int result;
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700293 try {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400294 result = pm.getPackageUid(app, mUserId);
295 } catch (NameNotFoundException e) {
296 result = -1;
297 }
298 return result;
299 }
300
301 public NetworkInfo getNetworkInfo() {
302 return mNetworkInfo;
303 }
304
305 private void agentConnect() {
306 LinkProperties lp = new LinkProperties();
307 lp.setInterfaceName(mInterface);
308 boolean hasDefaultRoute = false;
309 for (RouteInfo route : mConfig.routes) {
310 lp.addRoute(route);
311 if (route.isDefaultRoute()) hasDefaultRoute = true;
312 }
313 if (hasDefaultRoute) {
314 mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
315 } else {
316 mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
317 }
318 if (mConfig.dnsServers != null) {
319 for (String dnsServer : mConfig.dnsServers) {
320 lp.addDnsServer(InetAddress.parseNumericAddress(dnsServer));
321 }
322 }
323 // Concatenate search domains into a string.
324 StringBuilder buffer = new StringBuilder();
325 if (mConfig.searchDomains != null) {
326 for (String domain : mConfig.searchDomains) {
327 buffer.append(domain).append(' ');
328 }
329 }
330 lp.setDomains(buffer.toString().trim());
331 mNetworkInfo.setIsAvailable(true);
332 mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
333 long token = Binder.clearCallingIdentity();
334 try {
335 mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
336 mNetworkInfo, mNetworkCapabilities, lp, 0) {
337 public void unwanted() {
338 // We are user controlled, not driven by NetworkRequest.
339 };
340 };
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700341 } finally {
342 Binder.restoreCallingIdentity(token);
343 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400344 addVpnUserLocked(mUserId);
345 // If we are owner assign all Restricted Users to this VPN
346 if (mUserId == UserHandle.USER_OWNER) {
347 token = Binder.clearCallingIdentity();
348 List<UserInfo> users;
349 try {
350 users = UserManager.get(mContext).getUsers();
351 } finally {
352 Binder.restoreCallingIdentity(token);
353 }
354 for (UserInfo user : users) {
355 if (user.isRestricted()) {
356 addVpnUserLocked(user.id);
357 }
358 }
359 }
360 mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
361 }
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700362
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400363 private void agentDisconnect(NetworkInfo networkInfo, NetworkAgent networkAgent) {
364 networkInfo.setIsAvailable(false);
365 networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
366 if (networkAgent != null) {
367 networkAgent.sendNetworkInfo(networkInfo);
368 }
369 }
370
371 private void agentDisconnect(NetworkAgent networkAgent) {
372 NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
373 agentDisconnect(networkInfo, networkAgent);
374 }
375
376 private void agentDisconnect() {
377 if (mNetworkInfo.isConnected()) {
378 agentDisconnect(mNetworkInfo, mNetworkAgent);
379 mNetworkAgent = null;
380 }
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700381 }
382
383 /**
Chia-chi Yehe9107902011-07-02 01:48:50 -0700384 * Establish a VPN network and return the file descriptor of the VPN
385 * interface. This methods returns {@code null} if the application is
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700386 * revoked or not prepared.
Chia-chi Yehe9107902011-07-02 01:48:50 -0700387 *
388 * @param config The parameters to configure the network.
389 * @return The file descriptor of the VPN interface.
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700390 */
Chia-chi Yeh04ba25c2011-06-15 17:07:27 -0700391 public synchronized ParcelFileDescriptor establish(VpnConfig config) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700392 // Check if the caller is already prepared.
Chad Brubakerc2865192013-07-10 14:46:23 -0700393 UserManager mgr = UserManager.get(mContext);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400394 if (Binder.getCallingUid() != mOwnerUID) {
Chia-chi Yeh7b0b8342011-06-17 14:34:11 -0700395 return null;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700396 }
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700397 // Check if the service is properly declared.
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700398 Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
399 intent.setClassName(mPackage, config.user);
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700400 long token = Binder.clearCallingIdentity();
401 try {
Chad Brubakerc2865192013-07-10 14:46:23 -0700402 // Restricted users are not allowed to create VPNs, they are tied to Owner
403 UserInfo user = mgr.getUserInfo(mUserId);
Julia Reynoldsf5116d02014-07-01 11:10:41 -0400404 if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
Chad Brubakerc2865192013-07-10 14:46:23 -0700405 throw new SecurityException("Restricted users cannot establish VPNs");
406 }
407
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700408 ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
409 null, 0, mUserId);
410 if (info == null) {
411 throw new SecurityException("Cannot find " + config.user);
412 }
413 if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
414 throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
415 }
416 } catch (RemoteException e) {
417 throw new SecurityException("Cannot find " + config.user);
418 } finally {
419 Binder.restoreCallingIdentity(token);
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700420 }
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700421
Chad Brubaker850eb672014-03-21 21:02:47 +0000422 // Save the old config in case we need to go back.
423 VpnConfig oldConfig = mConfig;
424 String oldInterface = mInterface;
425 Connection oldConnection = mConnection;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400426 NetworkAgent oldNetworkAgent = mNetworkAgent;
427 mNetworkAgent = null;
428 List<UidRange> oldUsers = mVpnUsers;
Chad Brubaker850eb672014-03-21 21:02:47 +0000429
Chia-chi Yehe9107902011-07-02 01:48:50 -0700430 // Configure the interface. Abort if any of these steps fails.
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700431 ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700432 try {
Jeff Sharkey899223b2012-08-04 15:24:58 -0700433 updateState(DetailedState.CONNECTING, "establish");
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700434 String interfaze = jniGetName(tun.getFd());
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700435
Chad Brubakerc2865192013-07-10 14:46:23 -0700436 // TEMP use the old jni calls until there is support for netd address setting
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700437 StringBuilder builder = new StringBuilder();
438 for (LinkAddress address : config.addresses) {
439 builder.append(" " + address);
440 }
441 if (jniSetAddresses(interfaze, builder.toString()) < 1) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700442 throw new IllegalArgumentException("At least one address must be specified");
443 }
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700444 Connection connection = new Connection();
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700445 if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
446 new UserHandle(mUserId))) {
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700447 throw new IllegalStateException("Cannot bind " + config.user);
448 }
Chad Brubaker850eb672014-03-21 21:02:47 +0000449
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700450 mConnection = connection;
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700451 mInterface = interfaze;
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700452
453 // Fill more values.
454 config.user = mPackage;
455 config.interfaze = mInterface;
Chad Brubakerc2865192013-07-10 14:46:23 -0700456 config.startTime = SystemClock.elapsedRealtime();
457 mConfig = config;
Chad Brubaker850eb672014-03-21 21:02:47 +0000458
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700459 // Set up forwarding and DNS rules.
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400460 mVpnUsers = new ArrayList<UidRange>();
461 agentConnect();
Chad Brubaker850eb672014-03-21 21:02:47 +0000462
463 if (oldConnection != null) {
464 mContext.unbindService(oldConnection);
465 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400466 // Remove the old tun's user forwarding rules
467 // The new tun's user rules have already been added so they will take over
468 // as rules are deleted. This prevents data leakage as the rules are moved over.
469 agentDisconnect(oldNetworkAgent);
Chad Brubaker850eb672014-03-21 21:02:47 +0000470 if (oldInterface != null && !oldInterface.equals(interfaze)) {
Chad Brubaker850eb672014-03-21 21:02:47 +0000471 jniReset(oldInterface);
472 }
473 } catch (RuntimeException e) {
Chad Brubaker850eb672014-03-21 21:02:47 +0000474 IoUtils.closeQuietly(tun);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400475 agentDisconnect();
Chad Brubaker850eb672014-03-21 21:02:47 +0000476 // restore old state
477 mConfig = oldConfig;
478 mConnection = oldConnection;
479 mVpnUsers = oldUsers;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400480 mNetworkAgent = oldNetworkAgent;
Chad Brubaker850eb672014-03-21 21:02:47 +0000481 mInterface = oldInterface;
482 throw e;
Chad Brubakerc2865192013-07-10 14:46:23 -0700483 }
Chad Brubaker850eb672014-03-21 21:02:47 +0000484 Log.i(TAG, "Established by " + config.user + " on " + mInterface);
485
Jeff Sharkey899223b2012-08-04 15:24:58 -0700486 // TODO: ensure that contract class eventually marks as connected
487 updateState(DetailedState.AUTHENTICATING, "establish");
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700488 return tun;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700489 }
490
Chad Brubakerc0234532014-02-14 13:24:29 -0800491 /**
492 * Check if a given address is covered by the VPN's routing rules.
493 */
494 public boolean isAddressCovered(InetAddress address) {
495 synchronized (Vpn.this) {
496 if (!isRunningLocked()) {
497 return false;
498 }
499 return RouteInfo.selectBestRoute(mConfig.routes, address) != null;
500 }
501 }
502
Chad Brubakerc2865192013-07-10 14:46:23 -0700503 private boolean isRunningLocked() {
504 return mVpnUsers != null;
505 }
506
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400507 // Note: This function adds to mVpnUsers but does not publish list to NetworkAgent.
Chad Brubakerc2865192013-07-10 14:46:23 -0700508 private void addVpnUserLocked(int user) {
Chad Brubakerc2865192013-07-10 14:46:23 -0700509 if (!isRunningLocked()) {
510 throw new IllegalStateException("VPN is not active");
511 }
Robert Greenwalt69887e82013-09-24 11:05:57 -0700512
Chad Brubakerc2865192013-07-10 14:46:23 -0700513 // add the user
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400514 mVpnUsers.add(UidRange.createForUser(user));
Chad Brubakerc2865192013-07-10 14:46:23 -0700515
516 // show the notification
517 if (!mPackage.equals(VpnConfig.LEGACY_VPN)) {
518 // Load everything for the user's notification
519 PackageManager pm = mContext.getPackageManager();
520 ApplicationInfo app = null;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400521 final long token = Binder.clearCallingIdentity();
Chad Brubakerc2865192013-07-10 14:46:23 -0700522 try {
523 app = AppGlobals.getPackageManager().getApplicationInfo(mPackage, 0, mUserId);
524 } catch (RemoteException e) {
525 throw new IllegalStateException("Invalid application");
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400526 } finally {
527 Binder.restoreCallingIdentity(token);
Chad Brubakerc2865192013-07-10 14:46:23 -0700528 }
529 String label = app.loadLabel(pm).toString();
530 // Load the icon and convert it into a bitmap.
531 Drawable icon = app.loadIcon(pm);
532 Bitmap bitmap = null;
533 if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
534 int width = mContext.getResources().getDimensionPixelSize(
535 android.R.dimen.notification_large_icon_width);
536 int height = mContext.getResources().getDimensionPixelSize(
537 android.R.dimen.notification_large_icon_height);
538 icon.setBounds(0, 0, width, height);
539 bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
540 Canvas c = new Canvas(bitmap);
541 icon.draw(c);
542 c.setBitmap(null);
543 }
544 showNotification(label, bitmap, user);
545 } else {
546 showNotification(null, null, user);
547 }
548 }
549
550 private void removeVpnUserLocked(int user) {
Chad Brubakerc2865192013-07-10 14:46:23 -0700551 if (!isRunningLocked()) {
552 throw new IllegalStateException("VPN is not active");
553 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400554 UidRange uidRange = UidRange.createForUser(user);
555 if (mNetworkAgent != null) {
556 mNetworkAgent.removeUidRanges(new UidRange[] { uidRange });
557 }
558 mVpnUsers.remove(uidRange);
Chad Brubakerc2865192013-07-10 14:46:23 -0700559 hideNotification(user);
560 }
561
562 private void onUserAdded(int userId) {
563 // If the user is restricted tie them to the owner's VPN
564 synchronized(Vpn.this) {
565 UserManager mgr = UserManager.get(mContext);
566 UserInfo user = mgr.getUserInfo(userId);
567 if (user.isRestricted()) {
568 try {
569 addVpnUserLocked(userId);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400570 if (mNetworkAgent != null) {
571 UidRange uidRange = UidRange.createForUser(userId);
572 mNetworkAgent.addUidRanges(new UidRange[] { uidRange });
573 }
Chad Brubakerc2865192013-07-10 14:46:23 -0700574 } catch (Exception e) {
575 Log.wtf(TAG, "Failed to add restricted user to owner", e);
576 }
577 }
578 }
579 }
580
581 private void onUserRemoved(int userId) {
582 // clean up if restricted
583 synchronized(Vpn.this) {
584 UserManager mgr = UserManager.get(mContext);
585 UserInfo user = mgr.getUserInfo(userId);
586 if (user.isRestricted()) {
587 try {
588 removeVpnUserLocked(userId);
589 } catch (Exception e) {
590 Log.wtf(TAG, "Failed to remove restricted user to owner", e);
591 }
592 }
593 }
594 }
595
Chad Brubakerbf6ff2c2013-07-16 18:59:12 -0700596 /**
597 * Return the configuration of the currently running VPN.
598 */
599 public VpnConfig getVpnConfig() {
600 enforceControlPermission();
601 return mConfig;
602 }
603
Jeff Sharkey899223b2012-08-04 15:24:58 -0700604 @Deprecated
605 public synchronized void interfaceStatusChanged(String iface, boolean up) {
606 try {
607 mObserver.interfaceStatusChanged(iface, up);
608 } catch (RemoteException e) {
609 // ignored; target is local
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700610 }
611 }
612
Jeff Sharkey899223b2012-08-04 15:24:58 -0700613 private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
614 @Override
615 public void interfaceStatusChanged(String interfaze, boolean up) {
616 synchronized (Vpn.this) {
617 if (!up && mLegacyVpnRunner != null) {
618 mLegacyVpnRunner.check(interfaze);
619 }
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700620 }
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700621 }
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700622
Jeff Sharkey899223b2012-08-04 15:24:58 -0700623 @Override
624 public void interfaceRemoved(String interfaze) {
625 synchronized (Vpn.this) {
626 if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400627 for (UidRange uidRange : mVpnUsers) {
628 hideNotification(uidRange.getStartUser());
Jeff Sharkey899223b2012-08-04 15:24:58 -0700629 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400630 mVpnUsers = null;
Jeff Sharkey899223b2012-08-04 15:24:58 -0700631 mInterface = null;
632 if (mConnection != null) {
633 mContext.unbindService(mConnection);
634 mConnection = null;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400635 agentDisconnect();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700636 } else if (mLegacyVpnRunner != null) {
637 mLegacyVpnRunner.exit();
638 mLegacyVpnRunner = null;
639 }
640 }
641 }
642 }
643 };
Haoyu Baidb3c8672012-06-20 14:29:57 -0700644
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700645 private void enforceControlPermission() {
646 // System user is allowed to control VPN.
647 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
648 return;
649 }
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700650 int appId = UserHandle.getAppId(Binder.getCallingUid());
651 final long token = Binder.clearCallingIdentity();
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700652 try {
Nick Kralevich212a1952013-10-18 17:48:39 -0700653 // System VPN dialogs are also allowed to control VPN.
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700654 PackageManager pm = mContext.getPackageManager();
655 ApplicationInfo app = pm.getApplicationInfo(VpnConfig.DIALOGS_PACKAGE, 0);
Nick Kralevich212a1952013-10-18 17:48:39 -0700656 if (((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) && (appId == app.uid)) {
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700657 return;
658 }
659 } catch (Exception e) {
660 // ignore
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700661 } finally {
662 Binder.restoreCallingIdentity(token);
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700663 }
664
665 throw new SecurityException("Unauthorized Caller");
666 }
667
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700668 private class Connection implements ServiceConnection {
669 private IBinder mService;
670
671 @Override
672 public void onServiceConnected(ComponentName name, IBinder service) {
673 mService = service;
674 }
675
676 @Override
677 public void onServiceDisconnected(ComponentName name) {
678 mService = null;
679 }
680 }
681
Chad Brubakerc2865192013-07-10 14:46:23 -0700682 private void showNotification(String label, Bitmap icon, int user) {
Jeff Sharkey69ddab42012-08-25 00:05:46 -0700683 if (!mEnableNotif) return;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400684 final long token = Binder.clearCallingIdentity();
685 try {
686 mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
Jeff Sharkey899223b2012-08-04 15:24:58 -0700687
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400688 NotificationManager nm = (NotificationManager)
689 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700690
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400691 if (nm != null) {
692 String title = (label == null) ? mContext.getString(R.string.vpn_title) :
693 mContext.getString(R.string.vpn_title_long, label);
694 String text = (mConfig.session == null) ? mContext.getString(R.string.vpn_text) :
695 mContext.getString(R.string.vpn_text_long, mConfig.session);
Chia-chi Yeha4b87b52011-06-30 23:21:55 -0700696
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400697 Notification notification = new Notification.Builder(mContext)
698 .setSmallIcon(R.drawable.vpn_connected)
699 .setLargeIcon(icon)
700 .setContentTitle(title)
701 .setContentText(text)
702 .setContentIntent(mStatusIntent)
703 .setDefaults(0)
704 .setOngoing(true)
705 .build();
706 nm.notifyAsUser(null, R.drawable.vpn_connected, notification, new UserHandle(user));
707 }
708 } finally {
709 Binder.restoreCallingIdentity(token);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700710 }
711 }
712
Chad Brubakerc2865192013-07-10 14:46:23 -0700713 private void hideNotification(int user) {
Jeff Sharkey69ddab42012-08-25 00:05:46 -0700714 if (!mEnableNotif) return;
Jeff Sharkey899223b2012-08-04 15:24:58 -0700715 mStatusIntent = null;
716
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700717 NotificationManager nm = (NotificationManager)
718 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
719
720 if (nm != null) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400721 final long token = Binder.clearCallingIdentity();
722 try {
723 nm.cancelAsUser(null, R.drawable.vpn_connected, new UserHandle(user));
724 } finally {
725 Binder.restoreCallingIdentity(token);
726 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700727 }
728 }
729
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700730 private native int jniCreate(int mtu);
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700731 private native String jniGetName(int tun);
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700732 private native int jniSetAddresses(String interfaze, String addresses);
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700733 private native void jniReset(String interfaze);
734 private native int jniCheck(String interfaze);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700735
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +0900736 private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
737 for (RouteInfo route : prop.getAllRoutes()) {
Jeff Sharkey82f85212012-08-24 11:17:25 -0700738 // Currently legacy VPN only works on IPv4.
739 if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +0900740 return route;
Jeff Sharkey82f85212012-08-24 11:17:25 -0700741 }
742 }
Jeff Sharkey899223b2012-08-04 15:24:58 -0700743
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +0900744 throw new IllegalStateException("Unable to find IPv4 default gateway");
Jeff Sharkey82f85212012-08-24 11:17:25 -0700745 }
746
747 /**
748 * Start legacy VPN, controlling native daemons as needed. Creates a
749 * secondary thread to perform connection work, returning quickly.
750 */
751 public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
Robert Greenwalt5a6bdc42013-02-15 10:56:35 -0800752 enforceControlPermission();
Kenny Rootb9594ce2013-02-14 10:18:38 -0800753 if (!keyStore.isUnlocked()) {
Jeff Sharkey82f85212012-08-24 11:17:25 -0700754 throw new IllegalStateException("KeyStore isn't unlocked");
755 }
Julia Reynoldsf5116d02014-07-01 11:10:41 -0400756 UserManager mgr = UserManager.get(mContext);
757 UserInfo user = mgr.getUserInfo(mUserId);
758 if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
759 throw new SecurityException("Restricted users cannot establish VPNs");
760 }
Jeff Sharkey82f85212012-08-24 11:17:25 -0700761
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +0900762 final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
763 final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
764 final String iface = ipv4DefaultRoute.getInterface();
Jeff Sharkey82f85212012-08-24 11:17:25 -0700765
766 // Load certificates.
767 String privateKey = "";
768 String userCert = "";
769 String caCert = "";
770 String serverCert = "";
771 if (!profile.ipsecUserCert.isEmpty()) {
772 privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
773 byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
Elliott Hughesd396a442013-06-28 16:24:48 -0700774 userCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -0700775 }
776 if (!profile.ipsecCaCert.isEmpty()) {
777 byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
Elliott Hughesd396a442013-06-28 16:24:48 -0700778 caCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -0700779 }
780 if (!profile.ipsecServerCert.isEmpty()) {
781 byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
Elliott Hughesd396a442013-06-28 16:24:48 -0700782 serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -0700783 }
784 if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
785 throw new IllegalStateException("Cannot load credentials");
786 }
787
788 // Prepare arguments for racoon.
789 String[] racoon = null;
790 switch (profile.type) {
791 case VpnProfile.TYPE_L2TP_IPSEC_PSK:
792 racoon = new String[] {
793 iface, profile.server, "udppsk", profile.ipsecIdentifier,
794 profile.ipsecSecret, "1701",
795 };
796 break;
797 case VpnProfile.TYPE_L2TP_IPSEC_RSA:
798 racoon = new String[] {
799 iface, profile.server, "udprsa", privateKey, userCert,
800 caCert, serverCert, "1701",
801 };
802 break;
803 case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
804 racoon = new String[] {
805 iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
806 profile.ipsecSecret, profile.username, profile.password, "", gateway,
807 };
808 break;
809 case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
810 racoon = new String[] {
811 iface, profile.server, "xauthrsa", privateKey, userCert,
812 caCert, serverCert, profile.username, profile.password, "", gateway,
813 };
814 break;
815 case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
816 racoon = new String[] {
817 iface, profile.server, "hybridrsa",
818 caCert, serverCert, profile.username, profile.password, "", gateway,
819 };
820 break;
821 }
822
823 // Prepare arguments for mtpd.
824 String[] mtpd = null;
825 switch (profile.type) {
826 case VpnProfile.TYPE_PPTP:
827 mtpd = new String[] {
828 iface, "pptp", profile.server, "1723",
829 "name", profile.username, "password", profile.password,
830 "linkname", "vpn", "refuse-eap", "nodefaultroute",
831 "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
832 (profile.mppe ? "+mppe" : "nomppe"),
833 };
834 break;
835 case VpnProfile.TYPE_L2TP_IPSEC_PSK:
836 case VpnProfile.TYPE_L2TP_IPSEC_RSA:
837 mtpd = new String[] {
838 iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
839 "name", profile.username, "password", profile.password,
840 "linkname", "vpn", "refuse-eap", "nodefaultroute",
841 "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
842 };
843 break;
844 }
845
846 VpnConfig config = new VpnConfig();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700847 config.legacy = true;
Jeff Sharkey82f85212012-08-24 11:17:25 -0700848 config.user = profile.key;
849 config.interfaze = iface;
850 config.session = profile.name;
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700851
852 config.addLegacyRoutes(profile.routes);
Jeff Sharkey82f85212012-08-24 11:17:25 -0700853 if (!profile.dnsServers.isEmpty()) {
854 config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
855 }
856 if (!profile.searchDomains.isEmpty()) {
857 config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
858 }
Jeff Sharkey82f85212012-08-24 11:17:25 -0700859 startLegacyVpn(config, racoon, mtpd);
860 }
861
862 private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
863 stopLegacyVpn();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700864
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700865 // Prepare for the new request. This also checks the caller.
866 prepare(null, VpnConfig.LEGACY_VPN);
Jeff Sharkey899223b2012-08-04 15:24:58 -0700867 updateState(DetailedState.CONNECTING, "startLegacyVpn");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700868
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700869 // Start a new LegacyVpnRunner and we are done!
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700870 mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
871 mLegacyVpnRunner.start();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700872 }
873
Jeff Sharkey899223b2012-08-04 15:24:58 -0700874 public synchronized void stopLegacyVpn() {
875 if (mLegacyVpnRunner != null) {
876 mLegacyVpnRunner.exit();
877 mLegacyVpnRunner = null;
878
879 synchronized (LegacyVpnRunner.TAG) {
880 // wait for old thread to completely finish before spinning up
881 // new instance, otherwise state updates can be out of order.
882 }
883 }
884 }
885
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700886 /**
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700887 * Return the information of the current ongoing legacy VPN.
888 */
889 public synchronized LegacyVpnInfo getLegacyVpnInfo() {
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700890 // Check if the caller is authorized.
891 enforceControlPermission();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700892 if (mLegacyVpnRunner == null) return null;
893
894 final LegacyVpnInfo info = new LegacyVpnInfo();
Chad Brubakerc2865192013-07-10 14:46:23 -0700895 info.key = mConfig.user;
Jeff Sharkey899223b2012-08-04 15:24:58 -0700896 info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
897 if (mNetworkInfo.isConnected()) {
898 info.intent = mStatusIntent;
899 }
900 return info;
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700901 }
902
Jeff Sharkey69ddab42012-08-25 00:05:46 -0700903 public VpnConfig getLegacyVpnConfig() {
904 if (mLegacyVpnRunner != null) {
Chad Brubakerc2865192013-07-10 14:46:23 -0700905 return mConfig;
Jeff Sharkey69ddab42012-08-25 00:05:46 -0700906 } else {
907 return null;
908 }
909 }
910
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700911 /**
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700912 * Bringing up a VPN connection takes time, and that is all this thread
913 * does. Here we have plenty of time. The only thing we need to take
914 * care of is responding to interruptions as soon as possible. Otherwise
915 * requests will be piled up. This can be done in a Handler as a state
916 * machine, but it is much easier to read in the current form.
917 */
918 private class LegacyVpnRunner extends Thread {
919 private static final String TAG = "LegacyVpnRunner";
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700920
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700921 private final String[] mDaemons;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700922 private final String[][] mArguments;
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700923 private final LocalSocket[] mSockets;
Robert Greenwalt53c04bd2012-10-12 17:02:45 -0700924 private final String mOuterInterface;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700925 private final AtomicInteger mOuterConnection =
926 new AtomicInteger(ConnectivityManager.TYPE_NONE);
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700927
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700928 private long mTimer = -1;
929
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700930 /**
931 * Watch for the outer connection (passing in the constructor) going away.
932 */
933 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
934 @Override
935 public void onReceive(Context context, Intent intent) {
Jeff Sharkey57666932013-04-30 17:01:57 -0700936 if (!mEnableTeardown) return;
937
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700938 if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
939 if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
940 ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
941 NetworkInfo info = (NetworkInfo)intent.getExtra(
942 ConnectivityManager.EXTRA_NETWORK_INFO);
943 if (info != null && !info.isConnectedOrConnecting()) {
944 try {
945 mObserver.interfaceStatusChanged(mOuterInterface, false);
946 } catch (RemoteException e) {}
947 }
948 }
949 }
950 }
951 };
952
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700953 public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700954 super(TAG);
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700955 mConfig = config;
956 mDaemons = new String[] {"racoon", "mtpd"};
Jeff Sharkey899223b2012-08-04 15:24:58 -0700957 // TODO: clear arguments from memory once launched
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700958 mArguments = new String[][] {racoon, mtpd};
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700959 mSockets = new LocalSocket[mDaemons.length];
Robert Greenwalt53c04bd2012-10-12 17:02:45 -0700960
961 // This is the interface which VPN is running on,
962 // mConfig.interfaze will change to point to OUR
963 // internal interface soon. TODO - add inner/outer to mconfig
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700964 // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700965 // we will leave the VPN up. We should check that it's still there/connected after
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700966 // registering
Robert Greenwalt53c04bd2012-10-12 17:02:45 -0700967 mOuterInterface = mConfig.interfaze;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700968
969 try {
970 mOuterConnection.set(
971 mConnService.findConnectionTypeForIface(mOuterInterface));
972 } catch (Exception e) {
973 mOuterConnection.set(ConnectivityManager.TYPE_NONE);
974 }
975
976 IntentFilter filter = new IntentFilter();
977 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
978 mContext.registerReceiver(mBroadcastReceiver, filter);
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700979 }
980
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700981 public void check(String interfaze) {
Robert Greenwalt53c04bd2012-10-12 17:02:45 -0700982 if (interfaze.equals(mOuterInterface)) {
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700983 Log.i(TAG, "Legacy VPN is going down with " + interfaze);
984 exit();
985 }
986 }
987
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700988 public void exit() {
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700989 // We assume that everything is reset after stopping the daemons.
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700990 interrupt();
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700991 for (LocalSocket socket : mSockets) {
Jeff Sharkey065b2992012-08-05 14:16:48 -0700992 IoUtils.closeQuietly(socket);
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700993 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400994 agentDisconnect();
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700995 try {
996 mContext.unregisterReceiver(mBroadcastReceiver);
997 } catch (IllegalArgumentException e) {}
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700998 }
999
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001000 @Override
1001 public void run() {
1002 // Wait for the previous thread since it has been interrupted.
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001003 Log.v(TAG, "Waiting");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001004 synchronized (TAG) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001005 Log.v(TAG, "Executing");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001006 execute();
Jeff Sharkey899223b2012-08-04 15:24:58 -07001007 monitorDaemons();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001008 }
1009 }
1010
1011 private void checkpoint(boolean yield) throws InterruptedException {
1012 long now = SystemClock.elapsedRealtime();
1013 if (mTimer == -1) {
1014 mTimer = now;
1015 Thread.sleep(1);
Chia-chi Yeh7ef86112011-07-22 15:46:52 -07001016 } else if (now - mTimer <= 60000) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001017 Thread.sleep(yield ? 200 : 1);
1018 } else {
Jeff Sharkey899223b2012-08-04 15:24:58 -07001019 updateState(DetailedState.FAILED, "checkpoint");
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001020 throw new IllegalStateException("Time is up");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001021 }
1022 }
1023
1024 private void execute() {
1025 // Catch all exceptions so we can clean up few things.
Jeff Sharkey899223b2012-08-04 15:24:58 -07001026 boolean initFinished = false;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001027 try {
1028 // Initialize the timer.
1029 checkpoint(false);
1030
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001031 // Wait for the daemons to stop.
1032 for (String daemon : mDaemons) {
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001033 while (!SystemService.isStopped(daemon)) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001034 checkpoint(true);
1035 }
1036 }
1037
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001038 // Clear the previous state.
1039 File state = new File("/data/misc/vpn/state");
1040 state.delete();
1041 if (state.exists()) {
1042 throw new IllegalStateException("Cannot delete the state");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001043 }
Chia-chi Yehc1872732011-12-08 16:51:41 -08001044 new File("/data/misc/vpn/abort").delete();
Jeff Sharkey899223b2012-08-04 15:24:58 -07001045 initFinished = true;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001046
Chia-chi Yehe9107902011-07-02 01:48:50 -07001047 // Check if we need to restart any of the daemons.
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001048 boolean restart = false;
1049 for (String[] arguments : mArguments) {
1050 restart = restart || (arguments != null);
1051 }
1052 if (!restart) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001053 agentDisconnect();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001054 return;
1055 }
Jeff Sharkey899223b2012-08-04 15:24:58 -07001056 updateState(DetailedState.CONNECTING, "execute");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001057
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001058 // Start the daemon with arguments.
1059 for (int i = 0; i < mDaemons.length; ++i) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001060 String[] arguments = mArguments[i];
1061 if (arguments == null) {
1062 continue;
1063 }
1064
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001065 // Start the daemon.
1066 String daemon = mDaemons[i];
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001067 SystemService.start(daemon);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001068
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001069 // Wait for the daemon to start.
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001070 while (!SystemService.isRunning(daemon)) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001071 checkpoint(true);
1072 }
1073
1074 // Create the control socket.
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001075 mSockets[i] = new LocalSocket();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001076 LocalSocketAddress address = new LocalSocketAddress(
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001077 daemon, LocalSocketAddress.Namespace.RESERVED);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001078
1079 // Wait for the socket to connect.
1080 while (true) {
1081 try {
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001082 mSockets[i].connect(address);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001083 break;
1084 } catch (Exception e) {
1085 // ignore
1086 }
1087 checkpoint(true);
1088 }
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001089 mSockets[i].setSoTimeout(500);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001090
1091 // Send over the arguments.
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001092 OutputStream out = mSockets[i].getOutputStream();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001093 for (String argument : arguments) {
Elliott Hughesd396a442013-06-28 16:24:48 -07001094 byte[] bytes = argument.getBytes(StandardCharsets.UTF_8);
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001095 if (bytes.length >= 0xFFFF) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001096 throw new IllegalArgumentException("Argument is too large");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001097 }
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001098 out.write(bytes.length >> 8);
1099 out.write(bytes.length);
1100 out.write(bytes);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001101 checkpoint(false);
1102 }
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001103 out.write(0xFF);
1104 out.write(0xFF);
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001105 out.flush();
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001106
1107 // Wait for End-of-File.
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001108 InputStream in = mSockets[i].getInputStream();
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001109 while (true) {
1110 try {
1111 if (in.read() == -1) {
1112 break;
1113 }
1114 } catch (Exception e) {
1115 // ignore
1116 }
1117 checkpoint(true);
1118 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001119 }
1120
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001121 // Wait for the daemons to create the new state.
1122 while (!state.exists()) {
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001123 // Check if a running daemon is dead.
1124 for (int i = 0; i < mDaemons.length; ++i) {
1125 String daemon = mDaemons[i];
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001126 if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001127 throw new IllegalStateException(daemon + " is dead");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001128 }
1129 }
1130 checkpoint(true);
1131 }
1132
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001133 // Now we are connected. Read and parse the new state.
Chia-chi Yehc1bac3a2011-12-16 15:00:31 -08001134 String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001135 if (parameters.length != 6) {
1136 throw new IllegalStateException("Cannot parse the state");
1137 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001138
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001139 // Set the interface and the addresses in the config.
1140 mConfig.interfaze = parameters[0].trim();
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001141
Chad Brubaker4ca19e82013-06-14 11:16:51 -07001142 mConfig.addLegacyAddresses(parameters[1]);
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001143 // Set the routes if they are not set in the config.
1144 if (mConfig.routes == null || mConfig.routes.isEmpty()) {
Chad Brubaker4ca19e82013-06-14 11:16:51 -07001145 mConfig.addLegacyRoutes(parameters[2]);
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001146 }
1147
1148 // Set the DNS servers if they are not set in the config.
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001149 if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001150 String dnsServers = parameters[3].trim();
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001151 if (!dnsServers.isEmpty()) {
1152 mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
1153 }
1154 }
1155
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001156 // Set the search domains if they are not set in the config.
1157 if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
1158 String searchDomains = parameters[4].trim();
1159 if (!searchDomains.isEmpty()) {
1160 mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
1161 }
1162 }
Chia-chi Yehe9107902011-07-02 01:48:50 -07001163
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001164 // Here is the last step and it must be done synchronously.
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001165 synchronized (Vpn.this) {
Vinit Deshapnde2b862e52013-10-02 11:50:39 -07001166 // Set the start time
1167 mConfig.startTime = SystemClock.elapsedRealtime();
1168
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001169 // Check if the thread is interrupted while we are waiting.
1170 checkpoint(false);
1171
Chia-chi Yehe9107902011-07-02 01:48:50 -07001172 // Check if the interface is gone while we are waiting.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -07001173 if (jniCheck(mConfig.interfaze) == 0) {
Chia-chi Yeh34e78132011-07-03 03:07:07 -07001174 throw new IllegalStateException(mConfig.interfaze + " is gone");
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001175 }
Chia-chi Yehe9107902011-07-02 01:48:50 -07001176
1177 // Now INetworkManagementEventObserver is watching our back.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -07001178 mInterface = mConfig.interfaze;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001179 mVpnUsers = new ArrayList<UidRange>();
Chad Brubaker4ca19e82013-06-14 11:16:51 -07001180
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001181 agentConnect();
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001182
1183 Log.i(TAG, "Connected!");
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001184 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001185 } catch (Exception e) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001186 Log.i(TAG, "Aborting", e);
Chia-chi Yehe9107902011-07-02 01:48:50 -07001187 exit();
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001188 } finally {
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001189 // Kill the daemons if they fail to stop.
Jeff Sharkey899223b2012-08-04 15:24:58 -07001190 if (!initFinished) {
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001191 for (String daemon : mDaemons) {
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001192 SystemService.stop(daemon);
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001193 }
1194 }
1195
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001196 // Do not leave an unstable state.
Jeff Sharkey899223b2012-08-04 15:24:58 -07001197 if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001198 agentDisconnect();
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001199 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001200 }
1201 }
Jeff Sharkey899223b2012-08-04 15:24:58 -07001202
1203 /**
1204 * Monitor the daemons we started, moving to disconnected state if the
1205 * underlying services fail.
1206 */
1207 private void monitorDaemons() {
1208 if (!mNetworkInfo.isConnected()) {
1209 return;
1210 }
1211
1212 try {
1213 while (true) {
1214 Thread.sleep(2000);
1215 for (int i = 0; i < mDaemons.length; i++) {
1216 if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
1217 return;
1218 }
1219 }
1220 }
1221 } catch (InterruptedException e) {
1222 Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
1223 } finally {
1224 for (String daemon : mDaemons) {
1225 SystemService.stop(daemon);
1226 }
1227
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001228 agentDisconnect();
Jeff Sharkey899223b2012-08-04 15:24:58 -07001229 }
1230 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001231 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -07001232}