blob: ba9a1837a9768ce0f50e62a1c88c1aadc3733de8 [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;
Sreeram Ramachandran8cd33ed2014-07-23 15:23:15 -070051import android.net.NetworkMisc;
Chad Brubakerc0234532014-02-14 13:24:29 -080052import android.net.NetworkUtils;
Jeff Sharkey82f85212012-08-24 11:17:25 -070053import android.net.RouteInfo;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040054import android.net.UidRange;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070055import android.os.Binder;
Chia-chi Yehc1bac3a2011-12-16 15:00:31 -080056import android.os.FileUtils;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070057import android.os.IBinder;
Jeff Sharkey899223b2012-08-04 15:24:58 -070058import android.os.INetworkManagementService;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040059import android.os.Looper;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -070060import android.os.Parcel;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070061import android.os.ParcelFileDescriptor;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070062import android.os.Process;
Jeff Sharkey899223b2012-08-04 15:24:58 -070063import android.os.RemoteException;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070064import android.os.SystemClock;
Jeff Sharkey088f29f2012-08-05 14:55:04 -070065import android.os.SystemService;
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -070066import android.os.UserHandle;
Chad Brubakerc2865192013-07-10 14:46:23 -070067import android.os.UserManager;
Jeff Sharkey82f85212012-08-24 11:17:25 -070068import android.security.Credentials;
69import android.security.KeyStore;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070070import android.util.Log;
Chad Brubakerc2865192013-07-10 14:46:23 -070071import android.util.SparseBooleanArray;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070072
Chad Brubakerc2865192013-07-10 14:46:23 -070073import com.android.internal.annotations.GuardedBy;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070074import com.android.internal.R;
Chia-chi Yeh2e467642011-07-04 03:23:12 -070075import com.android.internal.net.LegacyVpnInfo;
Chia-chi Yeh04ba25c2011-06-15 17:07:27 -070076import com.android.internal.net.VpnConfig;
Jeff Sharkey82f85212012-08-24 11:17:25 -070077import com.android.internal.net.VpnProfile;
Jeff Sharkey899223b2012-08-04 15:24:58 -070078import com.android.server.net.BaseNetworkObserver;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070079
Chia-chi Yeh97a61562011-07-14 15:05:05 -070080import java.io.File;
Jeff Davidson6bbf39c2014-07-23 10:14:53 -070081import java.io.IOException;
Chia-chi Yeh97a61562011-07-14 15:05:05 -070082import java.io.InputStream;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070083import java.io.OutputStream;
Chad Brubakerc0234532014-02-14 13:24:29 -080084import java.net.InetAddress;
Jeff Sharkey82f85212012-08-24 11:17:25 -070085import java.net.Inet4Address;
Elliott Hughesd396a442013-06-28 16:24:48 -070086import java.nio.charset.StandardCharsets;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040087import java.util.ArrayList;
Chia-chi Yeh41d16852011-07-01 02:12:06 -070088import java.util.Arrays;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040089import java.util.List;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -070090import java.util.concurrent.atomic.AtomicInteger;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -070091
Jeff Sharkey065b2992012-08-05 14:16:48 -070092import libcore.io.IoUtils;
93
Chia-chi Yehff3bdca2011-05-23 17:26:46 -070094/**
95 * @hide
96 */
Paul Jensen6bc2c2c2014-05-07 15:27:40 -040097public class Vpn {
98 private static final String NETWORKTYPE = "VPN";
Jeff Sharkey899223b2012-08-04 15:24:58 -070099 private static final String TAG = "Vpn";
100 private static final boolean LOGD = true;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400101
Jeff Sharkey899223b2012-08-04 15:24:58 -0700102 // TODO: create separate trackers for each unique VPN to support
103 // automated reconnection
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700104
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400105 private Context mContext;
106 private NetworkInfo mNetworkInfo;
107 private String mPackage;
108 private int mOwnerUID;
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700109 private String mInterface;
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700110 private Connection mConnection;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700111 private LegacyVpnRunner mLegacyVpnRunner;
Jeff Sharkey899223b2012-08-04 15:24:58 -0700112 private PendingIntent mStatusIntent;
Jeff Sharkey57666932013-04-30 17:01:57 -0700113 private volatile boolean mEnableNotif = true;
114 private volatile boolean mEnableTeardown = true;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700115 private final IConnectivityManager mConnService;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400116 private final INetworkManagementService mNetd;
Chad Brubakerc2865192013-07-10 14:46:23 -0700117 private VpnConfig mConfig;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400118 private NetworkAgent mNetworkAgent;
119 private final Looper mLooper;
120 private final NetworkCapabilities mNetworkCapabilities;
Chad Brubakerc2865192013-07-10 14:46:23 -0700121
122 /* list of users using this VPN. */
123 @GuardedBy("this")
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400124 private List<UidRange> mVpnUsers = null;
Chad Brubakerc2865192013-07-10 14:46:23 -0700125 private BroadcastReceiver mUserIntentReceiver = null;
126
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700127 private final int mUserId;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700128
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400129 public Vpn(Looper looper, Context context, INetworkManagementService netService,
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700130 IConnectivityManager connService, int userId) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700131 mContext = context;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400132 mNetd = netService;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700133 mConnService = connService;
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700134 mUserId = userId;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400135 mLooper = looper;
136
137 mPackage = VpnConfig.LEGACY_VPN;
138 mOwnerUID = getAppUid(mPackage);
Jeff Sharkey899223b2012-08-04 15:24:58 -0700139
140 try {
141 netService.registerObserver(mObserver);
142 } catch (RemoteException e) {
143 Log.wtf(TAG, "Problem registering observer", e);
144 }
Chad Brubakerc2865192013-07-10 14:46:23 -0700145 if (userId == UserHandle.USER_OWNER) {
146 // Owner's VPN also needs to handle restricted users
147 mUserIntentReceiver = new BroadcastReceiver() {
148 @Override
149 public void onReceive(Context context, Intent intent) {
150 final String action = intent.getAction();
151 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
152 UserHandle.USER_NULL);
153 if (userId == UserHandle.USER_NULL) return;
154
155 if (Intent.ACTION_USER_ADDED.equals(action)) {
156 onUserAdded(userId);
157 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
158 onUserRemoved(userId);
159 }
160 }
161 };
162
163 IntentFilter intentFilter = new IntentFilter();
164 intentFilter.addAction(Intent.ACTION_USER_ADDED);
165 intentFilter.addAction(Intent.ACTION_USER_REMOVED);
166 mContext.registerReceiverAsUser(
167 mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
168 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400169
170 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
171 // TODO: Copy metered attribute and bandwidths from physical transport, b/16207332
172 mNetworkCapabilities = new NetworkCapabilities();
173 mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
174 mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
Jeff Sharkey899223b2012-08-04 15:24:58 -0700175 }
176
Jeff Sharkey57666932013-04-30 17:01:57 -0700177 /**
178 * Set if this object is responsible for showing its own notifications. When
179 * {@code false}, notifications are handled externally by someone else.
180 */
Jeff Sharkey69ddab42012-08-25 00:05:46 -0700181 public void setEnableNotifications(boolean enableNotif) {
182 mEnableNotif = enableNotif;
183 }
184
Jeff Sharkey57666932013-04-30 17:01:57 -0700185 /**
186 * Set if this object is responsible for watching for {@link NetworkInfo}
187 * teardown. When {@code false}, teardown is handled externally by someone
188 * else.
189 */
190 public void setEnableTeardown(boolean enableTeardown) {
191 mEnableTeardown = enableTeardown;
192 }
193
Jeff Sharkey899223b2012-08-04 15:24:58 -0700194 /**
195 * Update current state, dispaching event to listeners.
196 */
197 private void updateState(DetailedState detailedState, String reason) {
198 if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
199 mNetworkInfo.setDetailedState(detailedState, reason, null);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400200 if (mNetworkAgent != null) {
201 mNetworkAgent.sendNetworkInfo(mNetworkInfo);
202 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700203 }
204
205 /**
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700206 * Prepare for a VPN application. This method is designed to solve
207 * race conditions. It first compares the current prepared package
208 * with {@code oldPackage}. If they are the same, the prepared
209 * package is revoked and replaced with {@code newPackage}. If
210 * {@code oldPackage} is {@code null}, the comparison is omitted.
211 * If {@code newPackage} is the same package or {@code null}, the
212 * revocation is omitted. This method returns {@code true} if the
213 * operation is succeeded.
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700214 *
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700215 * Legacy VPN is handled specially since it is not a real package.
216 * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
217 * it can be revoked by itself.
218 *
219 * @param oldPackage The package name of the old VPN application.
220 * @param newPackage The package name of the new VPN application.
221 * @return true if the operation is succeeded.
Chia-chi Yehe9107902011-07-02 01:48:50 -0700222 */
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700223 public synchronized boolean prepare(String oldPackage, String newPackage) {
224 // Return false if the package does not match.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700225 if (oldPackage != null && !oldPackage.equals(mPackage)) {
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700226 return false;
Chia-chi Yehe9107902011-07-02 01:48:50 -0700227 }
228
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700229 // Return true if we do not need to revoke.
230 if (newPackage == null ||
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700231 (newPackage.equals(mPackage) && !newPackage.equals(VpnConfig.LEGACY_VPN))) {
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700232 return true;
233 }
234
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700235 // Check if the caller is authorized.
236 enforceControlPermission();
Chia-chi Yehe9107902011-07-02 01:48:50 -0700237
Chia-chi Yehe9107902011-07-02 01:48:50 -0700238 // Reset the interface and hide the notification.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700239 if (mInterface != null) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400240 for (UidRange uidRange : mVpnUsers) {
241 hideNotification(uidRange.getStartUser());
Jeff Sharkey899223b2012-08-04 15:24:58 -0700242 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400243 agentDisconnect();
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700244 jniReset(mInterface);
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700245 mInterface = null;
Chad Brubakerc2865192013-07-10 14:46:23 -0700246 mVpnUsers = null;
Chia-chi Yehe9107902011-07-02 01:48:50 -0700247 }
248
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700249 // Revoke the connection or stop LegacyVpnRunner.
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700250 if (mConnection != null) {
251 try {
252 mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
253 Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
254 } catch (Exception e) {
255 // ignore
256 }
257 mContext.unbindService(mConnection);
258 mConnection = null;
Chia-chi Yehe9107902011-07-02 01:48:50 -0700259 } else if (mLegacyVpnRunner != null) {
260 mLegacyVpnRunner.exit();
261 mLegacyVpnRunner = null;
262 }
263
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400264 long token = Binder.clearCallingIdentity();
265 try {
266 mNetd.denyProtect(mOwnerUID);
267 } catch (Exception e) {
268 Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
269 } finally {
270 Binder.restoreCallingIdentity(token);
271 }
272
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700273 Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
274 mPackage = newPackage;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400275 mOwnerUID = getAppUid(newPackage);
276 token = Binder.clearCallingIdentity();
277 try {
278 mNetd.allowProtect(mOwnerUID);
279 } catch (Exception e) {
280 Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
281 } finally {
282 Binder.restoreCallingIdentity(token);
283 }
Chad Brubakerc2865192013-07-10 14:46:23 -0700284 mConfig = null;
Jeff Sharkey899223b2012-08-04 15:24:58 -0700285 updateState(DetailedState.IDLE, "prepare");
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700286 return true;
Chia-chi Yehe9107902011-07-02 01:48:50 -0700287 }
288
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400289 private int getAppUid(String app) {
290 if (app == VpnConfig.LEGACY_VPN) {
291 return Process.myUid();
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700292 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400293 PackageManager pm = mContext.getPackageManager();
294 int result;
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700295 try {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400296 result = pm.getPackageUid(app, mUserId);
297 } catch (NameNotFoundException e) {
298 result = -1;
299 }
300 return result;
301 }
302
303 public NetworkInfo getNetworkInfo() {
304 return mNetworkInfo;
305 }
306
307 private void agentConnect() {
308 LinkProperties lp = new LinkProperties();
309 lp.setInterfaceName(mInterface);
310 boolean hasDefaultRoute = false;
311 for (RouteInfo route : mConfig.routes) {
312 lp.addRoute(route);
313 if (route.isDefaultRoute()) hasDefaultRoute = true;
314 }
315 if (hasDefaultRoute) {
316 mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
317 } else {
318 mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
319 }
320 if (mConfig.dnsServers != null) {
321 for (String dnsServer : mConfig.dnsServers) {
322 lp.addDnsServer(InetAddress.parseNumericAddress(dnsServer));
323 }
324 }
325 // Concatenate search domains into a string.
326 StringBuilder buffer = new StringBuilder();
327 if (mConfig.searchDomains != null) {
328 for (String domain : mConfig.searchDomains) {
329 buffer.append(domain).append(' ');
330 }
331 }
332 lp.setDomains(buffer.toString().trim());
333 mNetworkInfo.setIsAvailable(true);
334 mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
Sreeram Ramachandran8cd33ed2014-07-23 15:23:15 -0700335 NetworkMisc networkMisc = new NetworkMisc();
336 if (mConfig.allowBypass) {
337 networkMisc.allowBypass = true;
338 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400339 long token = Binder.clearCallingIdentity();
340 try {
341 mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
Sreeram Ramachandran8cd33ed2014-07-23 15:23:15 -0700342 mNetworkInfo, mNetworkCapabilities, lp, 0, networkMisc) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400343 public void unwanted() {
344 // We are user controlled, not driven by NetworkRequest.
345 };
346 };
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700347 } finally {
348 Binder.restoreCallingIdentity(token);
349 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400350 addVpnUserLocked(mUserId);
351 // If we are owner assign all Restricted Users to this VPN
352 if (mUserId == UserHandle.USER_OWNER) {
353 token = Binder.clearCallingIdentity();
354 List<UserInfo> users;
355 try {
356 users = UserManager.get(mContext).getUsers();
357 } finally {
358 Binder.restoreCallingIdentity(token);
359 }
360 for (UserInfo user : users) {
361 if (user.isRestricted()) {
362 addVpnUserLocked(user.id);
363 }
364 }
365 }
366 mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
367 }
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700368
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400369 private void agentDisconnect(NetworkInfo networkInfo, NetworkAgent networkAgent) {
370 networkInfo.setIsAvailable(false);
371 networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
372 if (networkAgent != null) {
373 networkAgent.sendNetworkInfo(networkInfo);
374 }
375 }
376
377 private void agentDisconnect(NetworkAgent networkAgent) {
378 NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
379 agentDisconnect(networkInfo, networkAgent);
380 }
381
382 private void agentDisconnect() {
383 if (mNetworkInfo.isConnected()) {
384 agentDisconnect(mNetworkInfo, mNetworkAgent);
385 mNetworkAgent = null;
386 }
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700387 }
388
389 /**
Chia-chi Yehe9107902011-07-02 01:48:50 -0700390 * Establish a VPN network and return the file descriptor of the VPN
391 * interface. This methods returns {@code null} if the application is
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700392 * revoked or not prepared.
Chia-chi Yehe9107902011-07-02 01:48:50 -0700393 *
394 * @param config The parameters to configure the network.
395 * @return The file descriptor of the VPN interface.
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700396 */
Chia-chi Yeh04ba25c2011-06-15 17:07:27 -0700397 public synchronized ParcelFileDescriptor establish(VpnConfig config) {
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700398 // Check if the caller is already prepared.
Chad Brubakerc2865192013-07-10 14:46:23 -0700399 UserManager mgr = UserManager.get(mContext);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400400 if (Binder.getCallingUid() != mOwnerUID) {
Chia-chi Yeh7b0b8342011-06-17 14:34:11 -0700401 return null;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700402 }
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700403 // Check if the service is properly declared.
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700404 Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
405 intent.setClassName(mPackage, config.user);
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700406 long token = Binder.clearCallingIdentity();
407 try {
Chad Brubakerc2865192013-07-10 14:46:23 -0700408 // Restricted users are not allowed to create VPNs, they are tied to Owner
409 UserInfo user = mgr.getUserInfo(mUserId);
Julia Reynoldsf5116d02014-07-01 11:10:41 -0400410 if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
Chad Brubakerc2865192013-07-10 14:46:23 -0700411 throw new SecurityException("Restricted users cannot establish VPNs");
412 }
413
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700414 ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
415 null, 0, mUserId);
416 if (info == null) {
417 throw new SecurityException("Cannot find " + config.user);
418 }
419 if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
420 throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
421 }
422 } catch (RemoteException e) {
423 throw new SecurityException("Cannot find " + config.user);
424 } finally {
425 Binder.restoreCallingIdentity(token);
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700426 }
Chia-chi Yehfcc1b412011-08-03 15:39:59 -0700427
Chad Brubaker850eb672014-03-21 21:02:47 +0000428 // Save the old config in case we need to go back.
429 VpnConfig oldConfig = mConfig;
430 String oldInterface = mInterface;
431 Connection oldConnection = mConnection;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400432 NetworkAgent oldNetworkAgent = mNetworkAgent;
433 mNetworkAgent = null;
434 List<UidRange> oldUsers = mVpnUsers;
Chad Brubaker850eb672014-03-21 21:02:47 +0000435
Chia-chi Yehe9107902011-07-02 01:48:50 -0700436 // Configure the interface. Abort if any of these steps fails.
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700437 ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700438 try {
Jeff Sharkey899223b2012-08-04 15:24:58 -0700439 updateState(DetailedState.CONNECTING, "establish");
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700440 String interfaze = jniGetName(tun.getFd());
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700441
Chad Brubakerc2865192013-07-10 14:46:23 -0700442 // TEMP use the old jni calls until there is support for netd address setting
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700443 StringBuilder builder = new StringBuilder();
444 for (LinkAddress address : config.addresses) {
445 builder.append(" " + address);
446 }
447 if (jniSetAddresses(interfaze, builder.toString()) < 1) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700448 throw new IllegalArgumentException("At least one address must be specified");
449 }
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700450 Connection connection = new Connection();
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700451 if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
452 new UserHandle(mUserId))) {
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700453 throw new IllegalStateException("Cannot bind " + config.user);
454 }
Chad Brubaker850eb672014-03-21 21:02:47 +0000455
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700456 mConnection = connection;
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700457 mInterface = interfaze;
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700458
459 // Fill more values.
460 config.user = mPackage;
461 config.interfaze = mInterface;
Chad Brubakerc2865192013-07-10 14:46:23 -0700462 config.startTime = SystemClock.elapsedRealtime();
463 mConfig = config;
Chad Brubaker850eb672014-03-21 21:02:47 +0000464
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700465 // Set up forwarding and DNS rules.
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400466 mVpnUsers = new ArrayList<UidRange>();
467 agentConnect();
Chad Brubaker850eb672014-03-21 21:02:47 +0000468
469 if (oldConnection != null) {
470 mContext.unbindService(oldConnection);
471 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400472 // Remove the old tun's user forwarding rules
473 // The new tun's user rules have already been added so they will take over
474 // as rules are deleted. This prevents data leakage as the rules are moved over.
475 agentDisconnect(oldNetworkAgent);
Chad Brubaker850eb672014-03-21 21:02:47 +0000476 if (oldInterface != null && !oldInterface.equals(interfaze)) {
Chad Brubaker850eb672014-03-21 21:02:47 +0000477 jniReset(oldInterface);
478 }
Jeff Davidson6bbf39c2014-07-23 10:14:53 -0700479
480 try {
481 IoUtils.setBlocking(tun.getFileDescriptor(), config.blocking);
482 } catch (IOException e) {
483 throw new IllegalStateException(
484 "Cannot set tunnel's fd as blocking=" + config.blocking, e);
485 }
Chad Brubaker850eb672014-03-21 21:02:47 +0000486 } catch (RuntimeException e) {
Chad Brubaker850eb672014-03-21 21:02:47 +0000487 IoUtils.closeQuietly(tun);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400488 agentDisconnect();
Chad Brubaker850eb672014-03-21 21:02:47 +0000489 // restore old state
490 mConfig = oldConfig;
491 mConnection = oldConnection;
492 mVpnUsers = oldUsers;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400493 mNetworkAgent = oldNetworkAgent;
Chad Brubaker850eb672014-03-21 21:02:47 +0000494 mInterface = oldInterface;
495 throw e;
Chad Brubakerc2865192013-07-10 14:46:23 -0700496 }
Chad Brubaker850eb672014-03-21 21:02:47 +0000497 Log.i(TAG, "Established by " + config.user + " on " + mInterface);
498
Jeff Sharkey899223b2012-08-04 15:24:58 -0700499 // TODO: ensure that contract class eventually marks as connected
500 updateState(DetailedState.AUTHENTICATING, "establish");
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700501 return tun;
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700502 }
503
Chad Brubakerc2865192013-07-10 14:46:23 -0700504 private boolean isRunningLocked() {
505 return mVpnUsers != null;
506 }
507
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400508 // Note: This function adds to mVpnUsers but does not publish list to NetworkAgent.
Chad Brubakerc2865192013-07-10 14:46:23 -0700509 private void addVpnUserLocked(int user) {
Chad Brubakerc2865192013-07-10 14:46:23 -0700510 if (!isRunningLocked()) {
511 throw new IllegalStateException("VPN is not active");
512 }
Robert Greenwalt69887e82013-09-24 11:05:57 -0700513
Chad Brubakerc2865192013-07-10 14:46:23 -0700514 // add the user
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400515 mVpnUsers.add(UidRange.createForUser(user));
Chad Brubakerc2865192013-07-10 14:46:23 -0700516
517 // show the notification
518 if (!mPackage.equals(VpnConfig.LEGACY_VPN)) {
519 // Load everything for the user's notification
520 PackageManager pm = mContext.getPackageManager();
521 ApplicationInfo app = null;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400522 final long token = Binder.clearCallingIdentity();
Chad Brubakerc2865192013-07-10 14:46:23 -0700523 try {
524 app = AppGlobals.getPackageManager().getApplicationInfo(mPackage, 0, mUserId);
525 } catch (RemoteException e) {
526 throw new IllegalStateException("Invalid application");
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400527 } finally {
528 Binder.restoreCallingIdentity(token);
Chad Brubakerc2865192013-07-10 14:46:23 -0700529 }
530 String label = app.loadLabel(pm).toString();
531 // Load the icon and convert it into a bitmap.
532 Drawable icon = app.loadIcon(pm);
533 Bitmap bitmap = null;
534 if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
535 int width = mContext.getResources().getDimensionPixelSize(
536 android.R.dimen.notification_large_icon_width);
537 int height = mContext.getResources().getDimensionPixelSize(
538 android.R.dimen.notification_large_icon_height);
539 icon.setBounds(0, 0, width, height);
540 bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
541 Canvas c = new Canvas(bitmap);
542 icon.draw(c);
543 c.setBitmap(null);
544 }
545 showNotification(label, bitmap, user);
546 } else {
547 showNotification(null, null, user);
548 }
549 }
550
551 private void removeVpnUserLocked(int user) {
Chad Brubakerc2865192013-07-10 14:46:23 -0700552 if (!isRunningLocked()) {
553 throw new IllegalStateException("VPN is not active");
554 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400555 UidRange uidRange = UidRange.createForUser(user);
556 if (mNetworkAgent != null) {
557 mNetworkAgent.removeUidRanges(new UidRange[] { uidRange });
558 }
559 mVpnUsers.remove(uidRange);
Chad Brubakerc2865192013-07-10 14:46:23 -0700560 hideNotification(user);
561 }
562
563 private void onUserAdded(int userId) {
564 // If the user is restricted tie them to the owner's VPN
565 synchronized(Vpn.this) {
566 UserManager mgr = UserManager.get(mContext);
567 UserInfo user = mgr.getUserInfo(userId);
568 if (user.isRestricted()) {
569 try {
570 addVpnUserLocked(userId);
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400571 if (mNetworkAgent != null) {
572 UidRange uidRange = UidRange.createForUser(userId);
573 mNetworkAgent.addUidRanges(new UidRange[] { uidRange });
574 }
Chad Brubakerc2865192013-07-10 14:46:23 -0700575 } catch (Exception e) {
576 Log.wtf(TAG, "Failed to add restricted user to owner", e);
577 }
578 }
579 }
580 }
581
582 private void onUserRemoved(int userId) {
583 // clean up if restricted
584 synchronized(Vpn.this) {
585 UserManager mgr = UserManager.get(mContext);
586 UserInfo user = mgr.getUserInfo(userId);
587 if (user.isRestricted()) {
588 try {
589 removeVpnUserLocked(userId);
590 } catch (Exception e) {
591 Log.wtf(TAG, "Failed to remove restricted user to owner", e);
592 }
593 }
594 }
595 }
596
Chad Brubakerbf6ff2c2013-07-16 18:59:12 -0700597 /**
598 * Return the configuration of the currently running VPN.
599 */
600 public VpnConfig getVpnConfig() {
601 enforceControlPermission();
602 return mConfig;
603 }
604
Jeff Sharkey899223b2012-08-04 15:24:58 -0700605 @Deprecated
606 public synchronized void interfaceStatusChanged(String iface, boolean up) {
607 try {
608 mObserver.interfaceStatusChanged(iface, up);
609 } catch (RemoteException e) {
610 // ignored; target is local
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700611 }
612 }
613
Jeff Sharkey899223b2012-08-04 15:24:58 -0700614 private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
615 @Override
616 public void interfaceStatusChanged(String interfaze, boolean up) {
617 synchronized (Vpn.this) {
618 if (!up && mLegacyVpnRunner != null) {
619 mLegacyVpnRunner.check(interfaze);
620 }
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700621 }
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700622 }
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700623
Jeff Sharkey899223b2012-08-04 15:24:58 -0700624 @Override
625 public void interfaceRemoved(String interfaze) {
626 synchronized (Vpn.this) {
627 if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400628 for (UidRange uidRange : mVpnUsers) {
629 hideNotification(uidRange.getStartUser());
Jeff Sharkey899223b2012-08-04 15:24:58 -0700630 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400631 mVpnUsers = null;
Jeff Sharkey899223b2012-08-04 15:24:58 -0700632 mInterface = null;
633 if (mConnection != null) {
634 mContext.unbindService(mConnection);
635 mConnection = null;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400636 agentDisconnect();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700637 } else if (mLegacyVpnRunner != null) {
638 mLegacyVpnRunner.exit();
639 mLegacyVpnRunner = null;
640 }
641 }
642 }
643 }
644 };
Haoyu Baidb3c8672012-06-20 14:29:57 -0700645
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700646 private void enforceControlPermission() {
647 // System user is allowed to control VPN.
648 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
649 return;
650 }
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700651 int appId = UserHandle.getAppId(Binder.getCallingUid());
652 final long token = Binder.clearCallingIdentity();
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700653 try {
Nick Kralevich212a1952013-10-18 17:48:39 -0700654 // System VPN dialogs are also allowed to control VPN.
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700655 PackageManager pm = mContext.getPackageManager();
656 ApplicationInfo app = pm.getApplicationInfo(VpnConfig.DIALOGS_PACKAGE, 0);
Nick Kralevich212a1952013-10-18 17:48:39 -0700657 if (((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) && (appId == app.uid)) {
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700658 return;
659 }
660 } catch (Exception e) {
661 // ignore
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700662 } finally {
663 Binder.restoreCallingIdentity(token);
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700664 }
665
666 throw new SecurityException("Unauthorized Caller");
667 }
668
Chia-chi Yeh199ed6e2011-08-03 17:38:49 -0700669 private class Connection implements ServiceConnection {
670 private IBinder mService;
671
672 @Override
673 public void onServiceConnected(ComponentName name, IBinder service) {
674 mService = service;
675 }
676
677 @Override
678 public void onServiceDisconnected(ComponentName name) {
679 mService = null;
680 }
681 }
682
Chad Brubakerc2865192013-07-10 14:46:23 -0700683 private void showNotification(String label, Bitmap icon, int user) {
Jeff Sharkey69ddab42012-08-25 00:05:46 -0700684 if (!mEnableNotif) return;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400685 final long token = Binder.clearCallingIdentity();
686 try {
687 mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
Jeff Sharkey899223b2012-08-04 15:24:58 -0700688
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400689 NotificationManager nm = (NotificationManager)
690 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700691
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400692 if (nm != null) {
693 String title = (label == null) ? mContext.getString(R.string.vpn_title) :
694 mContext.getString(R.string.vpn_title_long, label);
695 String text = (mConfig.session == null) ? mContext.getString(R.string.vpn_text) :
696 mContext.getString(R.string.vpn_text_long, mConfig.session);
Chia-chi Yeha4b87b52011-06-30 23:21:55 -0700697
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400698 Notification notification = new Notification.Builder(mContext)
699 .setSmallIcon(R.drawable.vpn_connected)
700 .setLargeIcon(icon)
701 .setContentTitle(title)
702 .setContentText(text)
703 .setContentIntent(mStatusIntent)
704 .setDefaults(0)
705 .setOngoing(true)
706 .build();
707 nm.notifyAsUser(null, R.drawable.vpn_connected, notification, new UserHandle(user));
708 }
709 } finally {
710 Binder.restoreCallingIdentity(token);
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700711 }
712 }
713
Chad Brubakerc2865192013-07-10 14:46:23 -0700714 private void hideNotification(int user) {
Jeff Sharkey69ddab42012-08-25 00:05:46 -0700715 if (!mEnableNotif) return;
Jeff Sharkey899223b2012-08-04 15:24:58 -0700716 mStatusIntent = null;
717
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700718 NotificationManager nm = (NotificationManager)
719 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
720
721 if (nm != null) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400722 final long token = Binder.clearCallingIdentity();
723 try {
724 nm.cancelAsUser(null, R.drawable.vpn_connected, new UserHandle(user));
725 } finally {
726 Binder.restoreCallingIdentity(token);
727 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -0700728 }
729 }
730
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700731 private native int jniCreate(int mtu);
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700732 private native String jniGetName(int tun);
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700733 private native int jniSetAddresses(String interfaze, String addresses);
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -0700734 private native void jniReset(String interfaze);
735 private native int jniCheck(String interfaze);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700736
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +0900737 private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
738 for (RouteInfo route : prop.getAllRoutes()) {
Jeff Sharkey82f85212012-08-24 11:17:25 -0700739 // Currently legacy VPN only works on IPv4.
740 if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +0900741 return route;
Jeff Sharkey82f85212012-08-24 11:17:25 -0700742 }
743 }
Jeff Sharkey899223b2012-08-04 15:24:58 -0700744
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +0900745 throw new IllegalStateException("Unable to find IPv4 default gateway");
Jeff Sharkey82f85212012-08-24 11:17:25 -0700746 }
747
748 /**
749 * Start legacy VPN, controlling native daemons as needed. Creates a
750 * secondary thread to perform connection work, returning quickly.
751 */
752 public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
Robert Greenwalt5a6bdc42013-02-15 10:56:35 -0800753 enforceControlPermission();
Kenny Rootb9594ce2013-02-14 10:18:38 -0800754 if (!keyStore.isUnlocked()) {
Jeff Sharkey82f85212012-08-24 11:17:25 -0700755 throw new IllegalStateException("KeyStore isn't unlocked");
756 }
Julia Reynoldsf5116d02014-07-01 11:10:41 -0400757 UserManager mgr = UserManager.get(mContext);
758 UserInfo user = mgr.getUserInfo(mUserId);
759 if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
760 throw new SecurityException("Restricted users cannot establish VPNs");
761 }
Jeff Sharkey82f85212012-08-24 11:17:25 -0700762
Lorenzo Colitti41fb98c2013-06-28 17:26:21 +0900763 final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
764 final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
765 final String iface = ipv4DefaultRoute.getInterface();
Jeff Sharkey82f85212012-08-24 11:17:25 -0700766
767 // Load certificates.
768 String privateKey = "";
769 String userCert = "";
770 String caCert = "";
771 String serverCert = "";
772 if (!profile.ipsecUserCert.isEmpty()) {
773 privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
774 byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
Elliott Hughesd396a442013-06-28 16:24:48 -0700775 userCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -0700776 }
777 if (!profile.ipsecCaCert.isEmpty()) {
778 byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
Elliott Hughesd396a442013-06-28 16:24:48 -0700779 caCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -0700780 }
781 if (!profile.ipsecServerCert.isEmpty()) {
782 byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
Elliott Hughesd396a442013-06-28 16:24:48 -0700783 serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
Jeff Sharkey82f85212012-08-24 11:17:25 -0700784 }
785 if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
786 throw new IllegalStateException("Cannot load credentials");
787 }
788
789 // Prepare arguments for racoon.
790 String[] racoon = null;
791 switch (profile.type) {
792 case VpnProfile.TYPE_L2TP_IPSEC_PSK:
793 racoon = new String[] {
794 iface, profile.server, "udppsk", profile.ipsecIdentifier,
795 profile.ipsecSecret, "1701",
796 };
797 break;
798 case VpnProfile.TYPE_L2TP_IPSEC_RSA:
799 racoon = new String[] {
800 iface, profile.server, "udprsa", privateKey, userCert,
801 caCert, serverCert, "1701",
802 };
803 break;
804 case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
805 racoon = new String[] {
806 iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
807 profile.ipsecSecret, profile.username, profile.password, "", gateway,
808 };
809 break;
810 case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
811 racoon = new String[] {
812 iface, profile.server, "xauthrsa", privateKey, userCert,
813 caCert, serverCert, profile.username, profile.password, "", gateway,
814 };
815 break;
816 case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
817 racoon = new String[] {
818 iface, profile.server, "hybridrsa",
819 caCert, serverCert, profile.username, profile.password, "", gateway,
820 };
821 break;
822 }
823
824 // Prepare arguments for mtpd.
825 String[] mtpd = null;
826 switch (profile.type) {
827 case VpnProfile.TYPE_PPTP:
828 mtpd = new String[] {
829 iface, "pptp", profile.server, "1723",
830 "name", profile.username, "password", profile.password,
831 "linkname", "vpn", "refuse-eap", "nodefaultroute",
832 "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
833 (profile.mppe ? "+mppe" : "nomppe"),
834 };
835 break;
836 case VpnProfile.TYPE_L2TP_IPSEC_PSK:
837 case VpnProfile.TYPE_L2TP_IPSEC_RSA:
838 mtpd = new String[] {
839 iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
840 "name", profile.username, "password", profile.password,
841 "linkname", "vpn", "refuse-eap", "nodefaultroute",
842 "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
843 };
844 break;
845 }
846
847 VpnConfig config = new VpnConfig();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700848 config.legacy = true;
Jeff Sharkey82f85212012-08-24 11:17:25 -0700849 config.user = profile.key;
850 config.interfaze = iface;
851 config.session = profile.name;
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700852
853 config.addLegacyRoutes(profile.routes);
Jeff Sharkey82f85212012-08-24 11:17:25 -0700854 if (!profile.dnsServers.isEmpty()) {
855 config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
856 }
857 if (!profile.searchDomains.isEmpty()) {
858 config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
859 }
Jeff Sharkey82f85212012-08-24 11:17:25 -0700860 startLegacyVpn(config, racoon, mtpd);
861 }
862
863 private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
864 stopLegacyVpn();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700865
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700866 // Prepare for the new request. This also checks the caller.
867 prepare(null, VpnConfig.LEGACY_VPN);
Jeff Sharkey899223b2012-08-04 15:24:58 -0700868 updateState(DetailedState.CONNECTING, "startLegacyVpn");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700869
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700870 // Start a new LegacyVpnRunner and we are done!
Chia-chi Yeh100155a2011-07-03 16:52:38 -0700871 mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
872 mLegacyVpnRunner.start();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700873 }
874
Jeff Sharkey899223b2012-08-04 15:24:58 -0700875 public synchronized void stopLegacyVpn() {
876 if (mLegacyVpnRunner != null) {
877 mLegacyVpnRunner.exit();
878 mLegacyVpnRunner = null;
879
880 synchronized (LegacyVpnRunner.TAG) {
881 // wait for old thread to completely finish before spinning up
882 // new instance, otherwise state updates can be out of order.
883 }
884 }
885 }
886
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700887 /**
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700888 * Return the information of the current ongoing legacy VPN.
889 */
890 public synchronized LegacyVpnInfo getLegacyVpnInfo() {
Chia-chi Yehdadc8572012-06-08 13:05:58 -0700891 // Check if the caller is authorized.
892 enforceControlPermission();
Jeff Sharkey899223b2012-08-04 15:24:58 -0700893 if (mLegacyVpnRunner == null) return null;
894
895 final LegacyVpnInfo info = new LegacyVpnInfo();
Chad Brubakerc2865192013-07-10 14:46:23 -0700896 info.key = mConfig.user;
Jeff Sharkey899223b2012-08-04 15:24:58 -0700897 info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
898 if (mNetworkInfo.isConnected()) {
899 info.intent = mStatusIntent;
900 }
901 return info;
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700902 }
903
Jeff Sharkey69ddab42012-08-25 00:05:46 -0700904 public VpnConfig getLegacyVpnConfig() {
905 if (mLegacyVpnRunner != null) {
Chad Brubakerc2865192013-07-10 14:46:23 -0700906 return mConfig;
Jeff Sharkey69ddab42012-08-25 00:05:46 -0700907 } else {
908 return null;
909 }
910 }
911
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700912 /**
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700913 * Bringing up a VPN connection takes time, and that is all this thread
914 * does. Here we have plenty of time. The only thing we need to take
915 * care of is responding to interruptions as soon as possible. Otherwise
916 * requests will be piled up. This can be done in a Handler as a state
917 * machine, but it is much easier to read in the current form.
918 */
919 private class LegacyVpnRunner extends Thread {
920 private static final String TAG = "LegacyVpnRunner";
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700921
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -0700922 private final String[] mDaemons;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700923 private final String[][] mArguments;
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700924 private final LocalSocket[] mSockets;
Robert Greenwalt53c04bd2012-10-12 17:02:45 -0700925 private final String mOuterInterface;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700926 private final AtomicInteger mOuterConnection =
927 new AtomicInteger(ConnectivityManager.TYPE_NONE);
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700928
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700929 private long mTimer = -1;
930
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700931 /**
932 * Watch for the outer connection (passing in the constructor) going away.
933 */
934 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
935 @Override
936 public void onReceive(Context context, Intent intent) {
Jeff Sharkey57666932013-04-30 17:01:57 -0700937 if (!mEnableTeardown) return;
938
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700939 if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
940 if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
941 ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
942 NetworkInfo info = (NetworkInfo)intent.getExtra(
943 ConnectivityManager.EXTRA_NETWORK_INFO);
944 if (info != null && !info.isConnectedOrConnecting()) {
945 try {
946 mObserver.interfaceStatusChanged(mOuterInterface, false);
947 } catch (RemoteException e) {}
948 }
949 }
950 }
951 }
952 };
953
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700954 public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -0700955 super(TAG);
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700956 mConfig = config;
957 mDaemons = new String[] {"racoon", "mtpd"};
Jeff Sharkey899223b2012-08-04 15:24:58 -0700958 // TODO: clear arguments from memory once launched
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700959 mArguments = new String[][] {racoon, mtpd};
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700960 mSockets = new LocalSocket[mDaemons.length];
Robert Greenwalt53c04bd2012-10-12 17:02:45 -0700961
962 // This is the interface which VPN is running on,
963 // mConfig.interfaze will change to point to OUR
964 // internal interface soon. TODO - add inner/outer to mconfig
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700965 // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
Chad Brubaker4ca19e82013-06-14 11:16:51 -0700966 // we will leave the VPN up. We should check that it's still there/connected after
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700967 // registering
Robert Greenwalt53c04bd2012-10-12 17:02:45 -0700968 mOuterInterface = mConfig.interfaze;
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700969
970 try {
971 mOuterConnection.set(
972 mConnService.findConnectionTypeForIface(mOuterInterface));
973 } catch (Exception e) {
974 mOuterConnection.set(ConnectivityManager.TYPE_NONE);
975 }
976
977 IntentFilter filter = new IntentFilter();
978 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
979 mContext.registerReceiver(mBroadcastReceiver, filter);
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700980 }
981
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700982 public void check(String interfaze) {
Robert Greenwalt53c04bd2012-10-12 17:02:45 -0700983 if (interfaze.equals(mOuterInterface)) {
Chia-chi Yehaa1727f2011-07-14 18:55:33 -0700984 Log.i(TAG, "Legacy VPN is going down with " + interfaze);
985 exit();
986 }
987 }
988
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700989 public void exit() {
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700990 // We assume that everything is reset after stopping the daemons.
Chia-chi Yeh97a61562011-07-14 15:05:05 -0700991 interrupt();
Chia-chi Yeh5317f032011-08-22 13:09:49 -0700992 for (LocalSocket socket : mSockets) {
Jeff Sharkey065b2992012-08-05 14:16:48 -0700993 IoUtils.closeQuietly(socket);
Chia-chi Yeh41d16852011-07-01 02:12:06 -0700994 }
Paul Jensen6bc2c2c2014-05-07 15:27:40 -0400995 agentDisconnect();
Robert Greenwalt1b0ca9d2013-04-22 11:13:02 -0700996 try {
997 mContext.unregisterReceiver(mBroadcastReceiver);
998 } catch (IllegalArgumentException e) {}
Chia-chi Yeh2e467642011-07-04 03:23:12 -0700999 }
1000
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001001 @Override
1002 public void run() {
1003 // Wait for the previous thread since it has been interrupted.
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001004 Log.v(TAG, "Waiting");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001005 synchronized (TAG) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001006 Log.v(TAG, "Executing");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001007 execute();
Jeff Sharkey899223b2012-08-04 15:24:58 -07001008 monitorDaemons();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001009 }
1010 }
1011
1012 private void checkpoint(boolean yield) throws InterruptedException {
1013 long now = SystemClock.elapsedRealtime();
1014 if (mTimer == -1) {
1015 mTimer = now;
1016 Thread.sleep(1);
Chia-chi Yeh7ef86112011-07-22 15:46:52 -07001017 } else if (now - mTimer <= 60000) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001018 Thread.sleep(yield ? 200 : 1);
1019 } else {
Jeff Sharkey899223b2012-08-04 15:24:58 -07001020 updateState(DetailedState.FAILED, "checkpoint");
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001021 throw new IllegalStateException("Time is up");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001022 }
1023 }
1024
1025 private void execute() {
1026 // Catch all exceptions so we can clean up few things.
Jeff Sharkey899223b2012-08-04 15:24:58 -07001027 boolean initFinished = false;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001028 try {
1029 // Initialize the timer.
1030 checkpoint(false);
1031
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001032 // Wait for the daemons to stop.
1033 for (String daemon : mDaemons) {
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001034 while (!SystemService.isStopped(daemon)) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001035 checkpoint(true);
1036 }
1037 }
1038
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001039 // Clear the previous state.
1040 File state = new File("/data/misc/vpn/state");
1041 state.delete();
1042 if (state.exists()) {
1043 throw new IllegalStateException("Cannot delete the state");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001044 }
Chia-chi Yehc1872732011-12-08 16:51:41 -08001045 new File("/data/misc/vpn/abort").delete();
Jeff Sharkey899223b2012-08-04 15:24:58 -07001046 initFinished = true;
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001047
Chia-chi Yehe9107902011-07-02 01:48:50 -07001048 // Check if we need to restart any of the daemons.
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001049 boolean restart = false;
1050 for (String[] arguments : mArguments) {
1051 restart = restart || (arguments != null);
1052 }
1053 if (!restart) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001054 agentDisconnect();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001055 return;
1056 }
Jeff Sharkey899223b2012-08-04 15:24:58 -07001057 updateState(DetailedState.CONNECTING, "execute");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001058
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001059 // Start the daemon with arguments.
1060 for (int i = 0; i < mDaemons.length; ++i) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001061 String[] arguments = mArguments[i];
1062 if (arguments == null) {
1063 continue;
1064 }
1065
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001066 // Start the daemon.
1067 String daemon = mDaemons[i];
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001068 SystemService.start(daemon);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001069
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001070 // Wait for the daemon to start.
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001071 while (!SystemService.isRunning(daemon)) {
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001072 checkpoint(true);
1073 }
1074
1075 // Create the control socket.
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001076 mSockets[i] = new LocalSocket();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001077 LocalSocketAddress address = new LocalSocketAddress(
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001078 daemon, LocalSocketAddress.Namespace.RESERVED);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001079
1080 // Wait for the socket to connect.
1081 while (true) {
1082 try {
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001083 mSockets[i].connect(address);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001084 break;
1085 } catch (Exception e) {
1086 // ignore
1087 }
1088 checkpoint(true);
1089 }
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001090 mSockets[i].setSoTimeout(500);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001091
1092 // Send over the arguments.
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001093 OutputStream out = mSockets[i].getOutputStream();
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001094 for (String argument : arguments) {
Elliott Hughesd396a442013-06-28 16:24:48 -07001095 byte[] bytes = argument.getBytes(StandardCharsets.UTF_8);
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001096 if (bytes.length >= 0xFFFF) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001097 throw new IllegalArgumentException("Argument is too large");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001098 }
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001099 out.write(bytes.length >> 8);
1100 out.write(bytes.length);
1101 out.write(bytes);
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001102 checkpoint(false);
1103 }
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001104 out.write(0xFF);
1105 out.write(0xFF);
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001106 out.flush();
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001107
1108 // Wait for End-of-File.
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001109 InputStream in = mSockets[i].getInputStream();
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001110 while (true) {
1111 try {
1112 if (in.read() == -1) {
1113 break;
1114 }
1115 } catch (Exception e) {
1116 // ignore
1117 }
1118 checkpoint(true);
1119 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001120 }
1121
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001122 // Wait for the daemons to create the new state.
1123 while (!state.exists()) {
Chia-chi Yeh1f7746b2011-07-01 00:29:06 -07001124 // Check if a running daemon is dead.
1125 for (int i = 0; i < mDaemons.length; ++i) {
1126 String daemon = mDaemons[i];
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001127 if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001128 throw new IllegalStateException(daemon + " is dead");
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001129 }
1130 }
1131 checkpoint(true);
1132 }
1133
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001134 // Now we are connected. Read and parse the new state.
Chia-chi Yehc1bac3a2011-12-16 15:00:31 -08001135 String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001136 if (parameters.length != 6) {
1137 throw new IllegalStateException("Cannot parse the state");
1138 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001139
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001140 // Set the interface and the addresses in the config.
1141 mConfig.interfaze = parameters[0].trim();
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001142
Chad Brubaker4ca19e82013-06-14 11:16:51 -07001143 mConfig.addLegacyAddresses(parameters[1]);
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001144 // Set the routes if they are not set in the config.
1145 if (mConfig.routes == null || mConfig.routes.isEmpty()) {
Chad Brubaker4ca19e82013-06-14 11:16:51 -07001146 mConfig.addLegacyRoutes(parameters[2]);
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001147 }
1148
1149 // Set the DNS servers if they are not set in the config.
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001150 if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001151 String dnsServers = parameters[3].trim();
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001152 if (!dnsServers.isEmpty()) {
1153 mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
1154 }
1155 }
1156
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001157 // Set the search domains if they are not set in the config.
1158 if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
1159 String searchDomains = parameters[4].trim();
1160 if (!searchDomains.isEmpty()) {
1161 mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
1162 }
1163 }
Chia-chi Yehe9107902011-07-02 01:48:50 -07001164
Chia-chi Yeh97a61562011-07-14 15:05:05 -07001165 // Here is the last step and it must be done synchronously.
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001166 synchronized (Vpn.this) {
Vinit Deshapnde2b862e52013-10-02 11:50:39 -07001167 // Set the start time
1168 mConfig.startTime = SystemClock.elapsedRealtime();
1169
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001170 // Check if the thread is interrupted while we are waiting.
1171 checkpoint(false);
1172
Chia-chi Yehe9107902011-07-02 01:48:50 -07001173 // Check if the interface is gone while we are waiting.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -07001174 if (jniCheck(mConfig.interfaze) == 0) {
Chia-chi Yeh34e78132011-07-03 03:07:07 -07001175 throw new IllegalStateException(mConfig.interfaze + " is gone");
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001176 }
Chia-chi Yehe9107902011-07-02 01:48:50 -07001177
1178 // Now INetworkManagementEventObserver is watching our back.
Chia-chi Yehc2b8aa02011-07-03 18:00:47 -07001179 mInterface = mConfig.interfaze;
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001180 mVpnUsers = new ArrayList<UidRange>();
Chad Brubaker4ca19e82013-06-14 11:16:51 -07001181
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001182 agentConnect();
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001183
1184 Log.i(TAG, "Connected!");
Chia-chi Yeh41d16852011-07-01 02:12:06 -07001185 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001186 } catch (Exception e) {
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001187 Log.i(TAG, "Aborting", e);
Chia-chi Yehe9107902011-07-02 01:48:50 -07001188 exit();
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001189 } finally {
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001190 // Kill the daemons if they fail to stop.
Jeff Sharkey899223b2012-08-04 15:24:58 -07001191 if (!initFinished) {
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001192 for (String daemon : mDaemons) {
Jeff Sharkey088f29f2012-08-05 14:55:04 -07001193 SystemService.stop(daemon);
Chia-chi Yeh5317f032011-08-22 13:09:49 -07001194 }
1195 }
1196
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001197 // Do not leave an unstable state.
Jeff Sharkey899223b2012-08-04 15:24:58 -07001198 if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001199 agentDisconnect();
Chia-chi Yeh2e467642011-07-04 03:23:12 -07001200 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001201 }
1202 }
Jeff Sharkey899223b2012-08-04 15:24:58 -07001203
1204 /**
1205 * Monitor the daemons we started, moving to disconnected state if the
1206 * underlying services fail.
1207 */
1208 private void monitorDaemons() {
1209 if (!mNetworkInfo.isConnected()) {
1210 return;
1211 }
1212
1213 try {
1214 while (true) {
1215 Thread.sleep(2000);
1216 for (int i = 0; i < mDaemons.length; i++) {
1217 if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
1218 return;
1219 }
1220 }
1221 }
1222 } catch (InterruptedException e) {
1223 Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
1224 } finally {
1225 for (String daemon : mDaemons) {
1226 SystemService.stop(daemon);
1227 }
1228
Paul Jensen6bc2c2c2014-05-07 15:27:40 -04001229 agentDisconnect();
Jeff Sharkey899223b2012-08-04 15:24:58 -07001230 }
1231 }
Chia-chi Yeh85a7ce02011-06-29 16:05:58 -07001232 }
Chia-chi Yehff3bdca2011-05-23 17:26:46 -07001233}