1ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh/*
2ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * Copyright (C) 2011 The Android Open Source Project
3ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh *
4ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * Licensed under the Apache License, Version 2.0 (the "License");
5ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * you may not use this file except in compliance with the License.
6ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * You may obtain a copy of the License at
7ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh *
8ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh *      http://www.apache.org/licenses/LICENSE-2.0
9ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh *
10ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * Unless required by applicable law or agreed to in writing, software
11ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * distributed under the License is distributed on an "AS IS" BASIS,
12ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * See the License for the specific language governing permissions and
14ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * limitations under the License.
15ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh */
16ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
17ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehpackage com.android.server.connectivity;
18ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
19899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport static android.Manifest.permission.BIND_VPN_SERVICE;
2031a94f48bf8014cf6a1127bd23cf9a8541a9abedPaul Jensenimport static android.net.ConnectivityManager.NETID_UNSET;
215026279ce45ae78126046607a2634dc9dae93199Lorenzo Colittiimport static android.net.RouteInfo.RTN_THROW;
2260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colittiimport static android.net.RouteInfo.RTN_UNREACHABLE;
23899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
24bc19c181c8c058c824e4fee907a05129e142c388Jeff Davidsonimport android.Manifest;
254d03abcd49af490dba3850d341b955dd72f24959Robin Leeimport android.annotation.NonNull;
264d03abcd49af490dba3850d341b955dd72f24959Robin Leeimport android.annotation.Nullable;
274d03abcd49af490dba3850d341b955dd72f24959Robin Leeimport android.annotation.UserIdInt;
284ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubakerimport android.app.AppGlobals;
2905542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidsonimport android.app.AppOpsManager;
301a405fe300950d6ceae2166fd074b596d8110dbeTony Makimport android.app.Notification;
311a405fe300950d6ceae2166fd074b596d8110dbeTony Makimport android.app.NotificationManager;
3290b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidsonimport android.app.PendingIntent;
331b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwaltimport android.content.BroadcastReceiver;
34199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.ComponentName;
35812800cb92090db31f609b907c4458ba76cf7f42Robin Leeimport android.content.ContentResolver;
36ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.Context;
37ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.Intent;
381b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwaltimport android.content.IntentFilter;
39199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.ServiceConnection;
40ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.content.pm.PackageManager;
416bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.content.pm.PackageManager.NameNotFoundException;
42199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.content.pm.ResolveInfo;
43c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubakerimport android.content.pm.UserInfo;
44899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.net.ConnectivityManager;
45ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.net.INetworkManagementEventObserver;
465026279ce45ae78126046607a2634dc9dae93199Lorenzo Colittiimport android.net.IpPrefix;
474ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubakerimport android.net.LinkAddress;
4882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.net.LinkProperties;
4985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.net.LocalSocket;
5085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.net.LocalSocketAddress;
51c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandranimport android.net.Network;
526bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.net.NetworkAgent;
536bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.net.NetworkCapabilities;
54899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.net.NetworkInfo;
556bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.net.NetworkInfo.DetailedState;
568cd33ed84e94036a5e1201485af7603dc6fb0d9bSreeram Ramachandranimport android.net.NetworkMisc;
5782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.net.RouteInfo;
586bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.net.UidRange;
59812800cb92090db31f609b907c4458ba76cf7f42Robin Leeimport android.net.Uri;
60ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.os.Binder;
61c1bac3a6e240c1c9a14a7b515f585977fb908930Chia-chi Yehimport android.os.FileUtils;
62199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.os.IBinder;
63899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.os.INetworkManagementService;
646bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport android.os.Looper;
65199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yehimport android.os.Parcel;
66ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.os.ParcelFileDescriptor;
67812800cb92090db31f609b907c4458ba76cf7f42Robin Leeimport android.os.PatternMatcher;
6885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.os.Process;
69899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport android.os.RemoteException;
7085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport android.os.SystemClock;
71088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkeyimport android.os.SystemService;
7250cdf7c3069eb2cf82acbad73c322b7a5f3af4b1Dianne Hackbornimport android.os.UserHandle;
73c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubakerimport android.os.UserManager;
74812800cb92090db31f609b907c4458ba76cf7f42Robin Leeimport android.provider.Settings;
7582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.security.Credentials;
7682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport android.security.KeyStore;
77e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensenimport android.text.TextUtils;
784d03abcd49af490dba3850d341b955dd72f24959Robin Leeimport android.util.ArraySet;
79ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yehimport android.util.Log;
80ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
811a405fe300950d6ceae2166fd074b596d8110dbeTony Makimport com.android.internal.R;
82c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubakerimport com.android.internal.annotations.GuardedBy;
834d03abcd49af490dba3850d341b955dd72f24959Robin Leeimport com.android.internal.annotations.VisibleForTesting;
84282cfefea0fbbd299839e353e6d30affdcd4a55cChris Wrenimport com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
852e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yehimport com.android.internal.net.LegacyVpnInfo;
8604ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yehimport com.android.internal.net.VpnConfig;
87f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tongimport com.android.internal.net.VpnInfo;
8882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport com.android.internal.net.VpnProfile;
89af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitschimport com.android.internal.notification.SystemNotificationChannels;
90e0be7e859b37438ec88780979e373821054a11efChristopher Tateimport com.android.server.DeviceIdleController;
91e0be7e859b37438ec88780979e373821054a11efChristopher Tateimport com.android.server.LocalServices;
92899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport com.android.server.net.BaseNetworkObserver;
93ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
9405542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidsonimport libcore.io.IoUtils;
9505542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
9697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yehimport java.io.File;
976bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidsonimport java.io.IOException;
9897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yehimport java.io.InputStream;
9985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport java.io.OutputStream;
10082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport java.net.Inet4Address;
101f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandranimport java.net.Inet6Address;
102f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandranimport java.net.InetAddress;
103d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughesimport java.nio.charset.StandardCharsets;
1046bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport java.util.ArrayList;
10541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yehimport java.util.Arrays;
1064d03abcd49af490dba3850d341b955dd72f24959Robin Leeimport java.util.Collection;
1074d03abcd49af490dba3850d341b955dd72f24959Robin Leeimport java.util.Collections;
1086bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport java.util.List;
1094d03abcd49af490dba3850d341b955dd72f24959Robin Leeimport java.util.Set;
1100784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensenimport java.util.SortedSet;
1110784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensenimport java.util.TreeSet;
1121b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwaltimport java.util.concurrent.atomic.AtomicInteger;
11385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
114ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh/**
115ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * @hide
116ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh */
1176bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenpublic class Vpn {
1186bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private static final String NETWORKTYPE = "VPN";
119899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private static final String TAG = "Vpn";
120899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private static final boolean LOGD = true;
1216bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
122e0be7e859b37438ec88780979e373821054a11efChristopher Tate    // Length of time (in milliseconds) that an app hosting an always-on VPN is placed on
123e0be7e859b37438ec88780979e373821054a11efChristopher Tate    // the device idle whitelist during service launch and VPN bootstrap.
124e0be7e859b37438ec88780979e373821054a11efChristopher Tate    private static final long VPN_LAUNCH_IDLE_WHITELIST_DURATION = 60 * 1000;
125e0be7e859b37438ec88780979e373821054a11efChristopher Tate
126899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    // TODO: create separate trackers for each unique VPN to support
127899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    // automated reconnection
128199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
1296bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private Context mContext;
1306bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private NetworkInfo mNetworkInfo;
1316bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private String mPackage;
1326bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private int mOwnerUID;
133c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private String mInterface;
134199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    private Connection mConnection;
13585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    private LegacyVpnRunner mLegacyVpnRunner;
13690b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson    private PendingIntent mStatusIntent;
13757666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    private volatile boolean mEnableTeardown = true;
1386bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private final INetworkManagementService mNetd;
139c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private VpnConfig mConfig;
1406bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private NetworkAgent mNetworkAgent;
1416bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private final Looper mLooper;
1426bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private final NetworkCapabilities mNetworkCapabilities;
143c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
1444d03abcd49af490dba3850d341b955dd72f24959Robin Lee    /**
14517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
14617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * only applies to {@link VpnService} connections.
14717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     */
14817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    private boolean mAlwaysOn = false;
14917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
15017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    /**
15117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * Whether to disable traffic outside of this VPN even when the VPN is not connected. System
15217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * apps can still bypass by choosing explicit networks. Has no effect if {@link mAlwaysOn} is
15317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * not set.
15417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     */
15517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    private boolean mLockdown = false;
15617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
15717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    /**
1584d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * List of UIDs that are set to use this VPN by default. Normally, every UID in the user is
1594d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * added to this set but that can be changed by adding allowed or disallowed applications. It
1604d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * is non-null iff the VPN is connected.
1614d03abcd49af490dba3850d341b955dd72f24959Robin Lee     *
1624d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * Unless the VPN has set allowBypass=true, these UIDs are forced into the VPN.
1634d03abcd49af490dba3850d341b955dd72f24959Robin Lee     *
1644d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @see VpnService.Builder#addAllowedApplication(String)
1654d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @see VpnService.Builder#addDisallowedApplication(String)
1664d03abcd49af490dba3850d341b955dd72f24959Robin Lee     */
167c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    @GuardedBy("this")
1684d03abcd49af490dba3850d341b955dd72f24959Robin Lee    private Set<UidRange> mVpnUsers = null;
169c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
17017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    /**
17117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * List of UIDs for which networking should be blocked until VPN is ready, during brief periods
17217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * when VPN is not running. For example, during system startup or after a crash.
17317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @see mLockdown
17417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     */
17517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    @GuardedBy("this")
17617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    private Set<UidRange> mBlockedUsers = new ArraySet<>();
17717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
1780784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    // Handle of user initiating VPN.
1790784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private final int mUserHandle;
180ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
181812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    // Listen to package remove and change event in this user
182812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
183812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        @Override
184812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        public void onReceive(Context context, Intent intent) {
185812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            final Uri data = intent.getData();
186812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            final String packageName = data == null ? null : data.getSchemeSpecificPart();
187812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            if (packageName == null) {
188812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                return;
189812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            }
190812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
191812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            synchronized (Vpn.this) {
192812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                // Avoid race that always-on package has been unset
193812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                if (!packageName.equals(getAlwaysOnPackage())) {
194812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                    return;
195812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                }
196812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
197812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                final String action = intent.getAction();
198812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                Log.i(TAG, "Received broadcast " + action + " for always-on package " + packageName
199812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                        + " in user " + mUserHandle);
200812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
201812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                switch(action) {
202812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                    case Intent.ACTION_PACKAGE_REPLACED:
203812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                        // Start vpn after app upgrade
204812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                        startAlwaysOnVpn();
205812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                        break;
206812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                    case Intent.ACTION_PACKAGE_REMOVED:
207812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                        final boolean isPackageRemoved = !intent.getBooleanExtra(
208812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                                Intent.EXTRA_REPLACING, false);
209812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                        if (isPackageRemoved) {
210812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                            setAndSaveAlwaysOnPackage(null, false);
211812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                        }
212812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                        break;
213812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                }
214812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            }
215812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        }
216812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    };
217812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
218812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    private boolean mIsPackageIntentReceiverRegistered = false;
219812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
2206bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    public Vpn(Looper looper, Context context, INetworkManagementService netService,
221e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen            int userHandle) {
222ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        mContext = context;
2236bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetd = netService;
2240784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        mUserHandle = userHandle;
2256bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mLooper = looper;
2266bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
2276bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mPackage = VpnConfig.LEGACY_VPN;
2280784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        mOwnerUID = getAppUid(mPackage, mUserHandle);
229899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
230899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        try {
231899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            netService.registerObserver(mObserver);
232899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        } catch (RemoteException e) {
233899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            Log.wtf(TAG, "Problem registering observer", e);
234899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
2356bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
2366bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
2376bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        // TODO: Copy metered attribute and bandwidths from physical transport, b/16207332
2386bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkCapabilities = new NetworkCapabilities();
2396bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
2406bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
241899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    }
242899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
24357666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    /**
24457666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * Set if this object is responsible for watching for {@link NetworkInfo}
24557666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * teardown. When {@code false}, teardown is handled externally by someone
24657666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * else.
24757666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     */
24857666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    public void setEnableTeardown(boolean enableTeardown) {
24957666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey        mEnableTeardown = enableTeardown;
25057666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    }
25157666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey
252899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    /**
253899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey     * Update current state, dispaching event to listeners.
254899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey     */
2551a405fe300950d6ceae2166fd074b596d8110dbeTony Mak    @VisibleForTesting
2561a405fe300950d6ceae2166fd074b596d8110dbeTony Mak    protected void updateState(DetailedState detailedState, String reason) {
257899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
258899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        mNetworkInfo.setDetailedState(detailedState, reason, null);
2596bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mNetworkAgent != null) {
2606bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
2616bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
2621a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        updateAlwaysOnNotification(detailedState);
263ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
264ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
265ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    /**
266244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     * Configures an always-on VPN connection through a specific application.
267244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     * This connection is automatically granted and persisted after a reboot.
268244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     *
269244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     * <p>The designated package should exist and declare a {@link VpnService} in its
270244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     *    manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
271244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     *    otherwise the call will fail.
272244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     *
27317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @param packageName the package to designate as always-on VPN supplier.
27417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
2759ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee     * @return {@code true} if the package has been set as always-on, {@code false} otherwise.
276244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     */
27717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    public synchronized boolean setAlwaysOnPackage(String packageName, boolean lockdown) {
278244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        enforceControlPermissionOrInternalCaller();
2799ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee        if (VpnConfig.LEGACY_VPN.equals(packageName)) {
2809ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee            Log.w(TAG, "Not setting legacy VPN \"" + packageName + "\" as always-on.");
2819ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee            return false;
2829ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee        }
283244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee
284244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        if (packageName != null) {
2859ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee            // Pre-authorize new always-on VPN package.
286244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee            if (!setPackageAuthorization(packageName, true)) {
287244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee                return false;
288244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee            }
2899ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee            mAlwaysOn = true;
2909ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee        } else {
2919ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee            packageName = VpnConfig.LEGACY_VPN;
2929ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee            mAlwaysOn = false;
293244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        }
294244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee
29517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        mLockdown = (mAlwaysOn && lockdown);
2961a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        if (isCurrentPreparedPackage(packageName)) {
2971a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            updateAlwaysOnNotification(mNetworkInfo.getDetailedState());
2981a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        } else {
2991a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            // Prepare this app. The notification will update as a side-effect of updateState().
3009ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee            prepareInternal(packageName);
3019ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee        }
302812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        maybeRegisterPackageChangeReceiverLocked(packageName);
30317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        setVpnForcedLocked(mLockdown);
304244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        return true;
305244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee    }
306244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee
3079ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee    private static boolean isNullOrLegacyVpn(String packageName) {
3089ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee        return packageName == null || VpnConfig.LEGACY_VPN.equals(packageName);
3099ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee    }
3109ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee
311812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    private void unregisterPackageChangeReceiverLocked() {
312812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        // register previous intent filter
313812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        if (mIsPackageIntentReceiverRegistered) {
314812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            mContext.unregisterReceiver(mPackageIntentReceiver);
315812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            mIsPackageIntentReceiverRegistered = false;
316812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        }
317812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    }
318812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
319812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    private void maybeRegisterPackageChangeReceiverLocked(String packageName) {
320812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        // Unregister IntentFilter listening for previous always-on package change
321812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        unregisterPackageChangeReceiverLocked();
322812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
3239ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee        if (!isNullOrLegacyVpn(packageName)) {
324812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            mIsPackageIntentReceiverRegistered = true;
325812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
326812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            IntentFilter intentFilter = new IntentFilter();
327812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            // Protected intent can only be sent by system. No permission required in register.
328812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
329812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
330812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            intentFilter.addDataScheme("package");
331812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            intentFilter.addDataSchemeSpecificPart(packageName, PatternMatcher.PATTERN_LITERAL);
332812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            mContext.registerReceiverAsUser(
333812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                    mPackageIntentReceiver, UserHandle.of(mUserHandle), intentFilter, null, null);
334812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        }
335812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    }
336812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
337244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee    /**
338244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     * @return the package name of the VPN controller responsible for always-on VPN,
339244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     *         or {@code null} if none is set or always-on VPN is controlled through
340244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     *         lockdown instead.
341244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     * @hide
342244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     */
343244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee    public synchronized String getAlwaysOnPackage() {
344244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        enforceControlPermissionOrInternalCaller();
34517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        return (mAlwaysOn ? mPackage : null);
346244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee    }
347244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee
348244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee    /**
349812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * Save the always-on package and lockdown config into Settings.Secure
350812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     */
351812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    public synchronized void saveAlwaysOnPackage() {
352812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        final long token = Binder.clearCallingIdentity();
353812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        try {
354812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            final ContentResolver cr = mContext.getContentResolver();
355812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            Settings.Secure.putStringForUser(cr, Settings.Secure.ALWAYS_ON_VPN_APP,
356812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                    getAlwaysOnPackage(), mUserHandle);
357812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            Settings.Secure.putIntForUser(cr, Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
358812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                    (mLockdown ? 1 : 0), mUserHandle);
359812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        } finally {
360812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            Binder.restoreCallingIdentity(token);
361812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        }
362812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    }
363812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
364812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    /**
365812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * Set and save always-on package and lockdown config
366812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * @see Vpn#setAlwaysOnPackage(String, boolean)
367812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * @see Vpn#saveAlwaysOnPackage()
368812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     *
369812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * @return result of Vpn#setAndSaveAlwaysOnPackage(String, boolean)
370812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     */
371812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    private synchronized boolean setAndSaveAlwaysOnPackage(String packageName, boolean lockdown) {
372812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        if (setAlwaysOnPackage(packageName, lockdown)) {
373812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            saveAlwaysOnPackage();
374812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            return true;
375812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        } else {
376812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            return false;
377812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        }
378812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    }
379812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
380812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    /**
381812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * @return {@code true} if the service was started, the service was already connected, or there
382812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     *         was no always-on VPN to start. {@code false} otherwise.
383812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     */
384812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    public boolean startAlwaysOnVpn() {
385812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        final String alwaysOnPackage;
386812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        synchronized (this) {
387812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            alwaysOnPackage = getAlwaysOnPackage();
388812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            // Skip if there is no service to start.
389812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            if (alwaysOnPackage == null) {
390812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                return true;
391812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            }
392812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            // Skip if the service is already established. This isn't bulletproof: it's not bound
393812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            // until after establish(), so if it's mid-setup onStartCommand will be sent twice,
394812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            // which may restart the connection.
395812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            if (getNetworkInfo().isConnected()) {
396812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                return true;
397812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            }
398812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        }
399812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
400e0be7e859b37438ec88780979e373821054a11efChristopher Tate        // Tell the OS that background services in this app need to be allowed for
401e0be7e859b37438ec88780979e373821054a11efChristopher Tate        // a short time, so we can bootstrap the VPN service.
402e0be7e859b37438ec88780979e373821054a11efChristopher Tate        final long oldId = Binder.clearCallingIdentity();
403812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        try {
404e0be7e859b37438ec88780979e373821054a11efChristopher Tate            DeviceIdleController.LocalService idleController =
405e0be7e859b37438ec88780979e373821054a11efChristopher Tate                    LocalServices.getService(DeviceIdleController.LocalService.class);
406e0be7e859b37438ec88780979e373821054a11efChristopher Tate            idleController.addPowerSaveTempWhitelistApp(Process.myUid(), alwaysOnPackage,
407e0be7e859b37438ec88780979e373821054a11efChristopher Tate                    VPN_LAUNCH_IDLE_WHITELIST_DURATION, mUserHandle, false, "vpn");
408e0be7e859b37438ec88780979e373821054a11efChristopher Tate
409e0be7e859b37438ec88780979e373821054a11efChristopher Tate            // Start the VPN service declared in the app's manifest.
410e0be7e859b37438ec88780979e373821054a11efChristopher Tate            Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
411e0be7e859b37438ec88780979e373821054a11efChristopher Tate            serviceIntent.setPackage(alwaysOnPackage);
412e0be7e859b37438ec88780979e373821054a11efChristopher Tate            try {
413e0be7e859b37438ec88780979e373821054a11efChristopher Tate                return mContext.startServiceAsUser(serviceIntent, UserHandle.of(mUserHandle)) != null;
414e0be7e859b37438ec88780979e373821054a11efChristopher Tate            } catch (RuntimeException e) {
415e0be7e859b37438ec88780979e373821054a11efChristopher Tate                Log.e(TAG, "VpnService " + serviceIntent + " failed to start", e);
416e0be7e859b37438ec88780979e373821054a11efChristopher Tate                return false;
417e0be7e859b37438ec88780979e373821054a11efChristopher Tate            }
418e0be7e859b37438ec88780979e373821054a11efChristopher Tate        } finally {
419e0be7e859b37438ec88780979e373821054a11efChristopher Tate            Binder.restoreCallingIdentity(oldId);
420812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        }
421812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    }
422812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
423812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    /**
424100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * Prepare for a VPN application. This method is designed to solve
425100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * race conditions. It first compares the current prepared package
426100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * with {@code oldPackage}. If they are the same, the prepared
427100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * package is revoked and replaced with {@code newPackage}. If
428100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * {@code oldPackage} is {@code null}, the comparison is omitted.
429100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * If {@code newPackage} is the same package or {@code null}, the
430100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * revocation is omitted. This method returns {@code true} if the
431100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * operation is succeeded.
432e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     *
433100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * Legacy VPN is handled specially since it is not a real package.
434100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
435100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * it can be revoked by itself.
436100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     *
43798a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     * Note: when we added VPN pre-consent in http://ag/522961 the names oldPackage
43898a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     * and newPackage become misleading, because when an app is pre-consented, we
43998a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     * actually prepare oldPackage, not newPackage.
44098a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     *
44198a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     * Their meanings actually are:
44298a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     *
44398a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     * - oldPackage non-null, newPackage null: App calling VpnService#prepare().
44498a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     * - oldPackage null, newPackage non-null: ConfirmDialog calling prepareVpn().
445812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * - oldPackage null, newPackage=LEGACY_VPN: Used internally to disconnect
44698a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     *   and revoke any current app VPN and re-prepare legacy vpn.
44798a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     *
448812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * TODO: Rename the variables - or split this method into two - and end this confusion.
449812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * TODO: b/29032008 Migrate code from prepare(oldPackage=non-null, newPackage=LEGACY_VPN)
450812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * to prepare(oldPackage=null, newPackage=LEGACY_VPN)
45198a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     *
45298a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     * @param oldPackage The package name of the old VPN application
45398a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     * @param newPackage The package name of the new VPN application
45498a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     *
455100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @return true if the operation is succeeded.
456ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
457100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh    public synchronized boolean prepare(String oldPackage, String newPackage) {
4580a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson        if (oldPackage != null) {
45998a633a89cf3223f79ea625323ec2b91bee72584Victor Chang            // Stop an existing always-on VPN from being dethroned by other apps.
460812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            if (mAlwaysOn && !isCurrentPreparedPackage(oldPackage)) {
46198a633a89cf3223f79ea625323ec2b91bee72584Victor Chang                return false;
46298a633a89cf3223f79ea625323ec2b91bee72584Victor Chang            }
46398a633a89cf3223f79ea625323ec2b91bee72584Victor Chang
46498a633a89cf3223f79ea625323ec2b91bee72584Victor Chang            // Package is not same or old package was reinstalled.
46598a633a89cf3223f79ea625323ec2b91bee72584Victor Chang            if (!isCurrentPreparedPackage(oldPackage)) {
4660a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                // The package doesn't match. We return false (to obtain user consent) unless the
4670a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                // user has already consented to that VPN package.
4680a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) {
4690a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                    prepareInternal(oldPackage);
4700a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                    return true;
4710a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                }
4720a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                return false;
4730a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson            } else if (!oldPackage.equals(VpnConfig.LEGACY_VPN)
4740a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                    && !isVpnUserPreConsented(oldPackage)) {
4750a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                // Currently prepared VPN is revoked, so unprepare it and return false.
4760a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                prepareInternal(VpnConfig.LEGACY_VPN);
4770a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                return false;
47805542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            }
479100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        }
480100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh
481100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Return true if we do not need to revoke.
482be08587510edbc149c841638db721eb97d2351b6Jeff Davidson        if (newPackage == null || (!newPackage.equals(VpnConfig.LEGACY_VPN) &&
48398a633a89cf3223f79ea625323ec2b91bee72584Victor Chang                isCurrentPreparedPackage(newPackage))) {
484100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh            return true;
485ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
486ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
4871b1bcd7b7370866bc00ce3361be8d5167bf3e28dRobin Lee        // Check that the caller is authorized.
488dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        enforceControlPermission();
4897b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh
49098a633a89cf3223f79ea625323ec2b91bee72584Victor Chang        // Stop an existing always-on VPN from being dethroned by other apps.
491812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        if (mAlwaysOn && !isCurrentPreparedPackage(newPackage)) {
49298a633a89cf3223f79ea625323ec2b91bee72584Victor Chang            return false;
49398a633a89cf3223f79ea625323ec2b91bee72584Victor Chang        }
49498a633a89cf3223f79ea625323ec2b91bee72584Victor Chang
49511008a78b8e30910cedd8b8431980c7738183292Jeff Davidson        prepareInternal(newPackage);
49611008a78b8e30910cedd8b8431980c7738183292Jeff Davidson        return true;
49711008a78b8e30910cedd8b8431980c7738183292Jeff Davidson    }
49811008a78b8e30910cedd8b8431980c7738183292Jeff Davidson
49998a633a89cf3223f79ea625323ec2b91bee72584Victor Chang    private boolean isCurrentPreparedPackage(String packageName) {
50098a633a89cf3223f79ea625323ec2b91bee72584Victor Chang        // We can't just check that packageName matches mPackage, because if the app was uninstalled
50198a633a89cf3223f79ea625323ec2b91bee72584Victor Chang        // and reinstalled it will no longer be prepared. Instead check the UID.
50298a633a89cf3223f79ea625323ec2b91bee72584Victor Chang        return getAppUid(packageName, mUserHandle) == mOwnerUID;
50398a633a89cf3223f79ea625323ec2b91bee72584Victor Chang    }
50498a633a89cf3223f79ea625323ec2b91bee72584Victor Chang
50511008a78b8e30910cedd8b8431980c7738183292Jeff Davidson    /** Prepare the VPN for the given package. Does not perform permission checks. */
50611008a78b8e30910cedd8b8431980c7738183292Jeff Davidson    private void prepareInternal(String newPackage) {
50711008a78b8e30910cedd8b8431980c7738183292Jeff Davidson        long token = Binder.clearCallingIdentity();
50811008a78b8e30910cedd8b8431980c7738183292Jeff Davidson        try {
50911008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            // Reset the interface.
51011008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            if (mInterface != null) {
51111008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mStatusIntent = null;
51211008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                agentDisconnect();
51311008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                jniReset(mInterface);
51411008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mInterface = null;
51511008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mVpnUsers = null;
51611008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            }
51711008a78b8e30910cedd8b8431980c7738183292Jeff Davidson
51811008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            // Revoke the connection or stop LegacyVpnRunner.
51911008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            if (mConnection != null) {
52011008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                try {
52111008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                    mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
52211008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                            Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
52311008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                } catch (Exception e) {
52411008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                    // ignore
52511008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                }
52611008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mContext.unbindService(mConnection);
52711008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mConnection = null;
52811008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            } else if (mLegacyVpnRunner != null) {
52911008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mLegacyVpnRunner.exit();
53011008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mLegacyVpnRunner = null;
53111008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            }
532ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
533199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            try {
53411008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mNetd.denyProtect(mOwnerUID);
535199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            } catch (Exception e) {
53611008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
537199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
53841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
53911008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
54011008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            mPackage = newPackage;
54111008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            mOwnerUID = getAppUid(newPackage, mUserHandle);
54211008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            try {
54311008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mNetd.allowProtect(mOwnerUID);
54411008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            } catch (Exception e) {
54511008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
54611008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            }
54711008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            mConfig = null;
5486bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
54911008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            updateState(DetailedState.IDLE, "prepare");
5506bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } finally {
5516bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            Binder.restoreCallingIdentity(token);
5526bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
553ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
554ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
55505542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    /**
5563b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee     * Set whether a package has the ability to launch VPNs without user intervention.
55705542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson     */
558244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee    public boolean setPackageAuthorization(String packageName, boolean authorized) {
55905542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        // Check if the caller is authorized.
560244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        enforceControlPermissionOrInternalCaller();
56105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
5623b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee        int uid = getAppUid(packageName, mUserHandle);
5633b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee        if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
5643b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee            // Authorization for nonexistent packages (or fake ones) can't be updated.
565244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee            return false;
56605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        }
56705542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
56805542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        long token = Binder.clearCallingIdentity();
56905542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        try {
57005542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            AppOpsManager appOps =
57105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                    (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
5723b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee            appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, uid, packageName,
57305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                    authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
574244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee            return true;
57505542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        } catch (Exception e) {
5763b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee            Log.wtf(TAG, "Failed to set app ops for package " + packageName + ", uid " + uid, e);
57705542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        } finally {
57805542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            Binder.restoreCallingIdentity(token);
57905542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        }
580244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        return false;
58105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    }
58205542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
58305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    private boolean isVpnUserPreConsented(String packageName) {
58405542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        AppOpsManager appOps =
58505542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
58605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
58705542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        // Verify that the caller matches the given package and has permission to activate VPNs.
58805542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        return appOps.noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Binder.getCallingUid(),
58905542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                packageName) == AppOpsManager.MODE_ALLOWED;
59005542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    }
59105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
5920784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private int getAppUid(String app, int userHandle) {
59305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        if (VpnConfig.LEGACY_VPN.equals(app)) {
5946bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            return Process.myUid();
5956bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
596fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        PackageManager pm = mContext.getPackageManager();
5976bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        int result;
5986bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        try {
599e06b4d1d9f718b9fe02980fea794a36831a16db2Jeff Sharkey            result = pm.getPackageUidAsUser(app, userHandle);
6006bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } catch (NameNotFoundException e) {
6016bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            result = -1;
602fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        }
6036bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        return result;
6046bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
6056bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
6066bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    public NetworkInfo getNetworkInfo() {
6076bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        return mNetworkInfo;
6086bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
6096bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
61031a94f48bf8014cf6a1127bd23cf9a8541a9abedPaul Jensen    public int getNetId() {
61131a94f48bf8014cf6a1127bd23cf9a8541a9abedPaul Jensen        return mNetworkAgent != null ? mNetworkAgent.netId : NETID_UNSET;
61231a94f48bf8014cf6a1127bd23cf9a8541a9abedPaul Jensen    }
61331a94f48bf8014cf6a1127bd23cf9a8541a9abedPaul Jensen
61460446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti    private LinkProperties makeLinkProperties() {
61560446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        boolean allowIPv4 = mConfig.allowIPv4;
61660446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        boolean allowIPv6 = mConfig.allowIPv6;
61760446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
6186bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        LinkProperties lp = new LinkProperties();
61960446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
6206bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        lp.setInterfaceName(mInterface);
62142065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
62260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        if (mConfig.addresses != null) {
62360446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            for (LinkAddress address : mConfig.addresses) {
62460446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                lp.addLinkAddress(address);
62560446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv4 |= address.getAddress() instanceof Inet4Address;
62660446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv6 |= address.getAddress() instanceof Inet6Address;
62760446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            }
6286bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
62960446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
63060446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        if (mConfig.routes != null) {
63160446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            for (RouteInfo route : mConfig.routes) {
63260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                lp.addRoute(route);
63360446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                InetAddress address = route.getDestination().getAddress();
63460446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv4 |= address instanceof Inet4Address;
63560446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv6 |= address instanceof Inet6Address;
63660446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            }
6376bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
63842065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
6396bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mConfig.dnsServers != null) {
6406bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            for (String dnsServer : mConfig.dnsServers) {
64142065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran                InetAddress address = InetAddress.parseNumericAddress(dnsServer);
64242065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran                lp.addDnsServer(address);
64360446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv4 |= address instanceof Inet4Address;
64460446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv6 |= address instanceof Inet6Address;
6456bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
6466bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
64742065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
64860446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        if (!allowIPv4) {
64960446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
65060446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        }
65160446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        if (!allowIPv6) {
65260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
65360446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        }
65460446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
6556bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        // Concatenate search domains into a string.
6566bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        StringBuilder buffer = new StringBuilder();
6576bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mConfig.searchDomains != null) {
6586bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            for (String domain : mConfig.searchDomains) {
6596bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                buffer.append(domain).append(' ');
6606bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
6616bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
6626bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        lp.setDomains(buffer.toString().trim());
66342065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
66460446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        // TODO: Stop setting the MTU in jniCreate and set it here.
66560446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
66660446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        return lp;
66760446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti    }
66860446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
66960446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti    private void agentConnect() {
67060446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        LinkProperties lp = makeLinkProperties();
67160446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
67260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        if (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute()) {
67360446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
67460446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        } else {
67560446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
67660446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        }
67760446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
678323f29df583e9338e3b2bf90fc8c0785a934a61bRobin Lee        mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null);
67942065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
6808cd33ed84e94036a5e1201485af7603dc6fb0d9bSreeram Ramachandran        NetworkMisc networkMisc = new NetworkMisc();
68117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        networkMisc.allowBypass = mConfig.allowBypass && !mLockdown;
68242065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
6836bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        long token = Binder.clearCallingIdentity();
6844ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        try {
6856bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
6868cd33ed84e94036a5e1201485af7603dc6fb0d9bSreeram Ramachandran                    mNetworkInfo, mNetworkCapabilities, lp, 0, networkMisc) {
68705542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                            @Override
6886bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                            public void unwanted() {
6896bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                                // We are user controlled, not driven by NetworkRequest.
69005542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                            }
6916bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        };
6924ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } finally {
6934ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            Binder.restoreCallingIdentity(token);
6944ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        }
69542065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
6964d03abcd49af490dba3850d341b955dd72f24959Robin Lee        mVpnUsers = createUserAndRestrictedProfilesRanges(mUserHandle,
6974d03abcd49af490dba3850d341b955dd72f24959Robin Lee                mConfig.allowedApplications, mConfig.disallowedApplications);
6986bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
699323f29df583e9338e3b2bf90fc8c0785a934a61bRobin Lee
700323f29df583e9338e3b2bf90fc8c0785a934a61bRobin Lee        mNetworkInfo.setIsAvailable(true);
701323f29df583e9338e3b2bf90fc8c0785a934a61bRobin Lee        updateState(DetailedState.CONNECTED, "agentConnect");
7026bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
7036bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
7041c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov    private boolean canHaveRestrictedProfile(int userId) {
7051c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov        long token = Binder.clearCallingIdentity();
7061c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov        try {
7071c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov            return UserManager.get(mContext).canHaveRestrictedProfile(userId);
7081c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov        } finally {
7091c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov            Binder.restoreCallingIdentity(token);
7101c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov        }
7111c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov    }
7121c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov
7131a405fe300950d6ceae2166fd074b596d8110dbeTony Mak    private void agentDisconnect(NetworkAgent networkAgent) {
7146bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (networkAgent != null) {
7151a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
7161a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            networkInfo.setIsAvailable(false);
7171a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
7186bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            networkAgent.sendNetworkInfo(networkInfo);
7196bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
7206bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
7216bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
7226bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private void agentDisconnect() {
7236bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mNetworkInfo.isConnected()) {
7241a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            mNetworkInfo.setIsAvailable(false);
7251a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            updateState(DetailedState.DISCONNECTED, "agentDisconnect");
7266bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent = null;
7276bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
728fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    }
729fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh
730fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    /**
731e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * Establish a VPN network and return the file descriptor of the VPN
732e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * interface. This methods returns {@code null} if the application is
733100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * revoked or not prepared.
734ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     *
735e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @param config The parameters to configure the network.
736e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @return The file descriptor of the VPN interface.
737ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
73804ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yeh    public synchronized ParcelFileDescriptor establish(VpnConfig config) {
739ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        // Check if the caller is already prepared.
740c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        UserManager mgr = UserManager.get(mContext);
7416bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (Binder.getCallingUid() != mOwnerUID) {
7427b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            return null;
743ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
7440a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson        // Check to ensure consent hasn't been revoked since we were prepared.
7450a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson        if (!isVpnUserPreConsented(mPackage)) {
7460a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson            return null;
7470a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson        }
748fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        // Check if the service is properly declared.
749199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
750199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        intent.setClassName(mPackage, config.user);
7514ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        long token = Binder.clearCallingIdentity();
7524ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        try {
753c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            // Restricted users are not allowed to create VPNs, they are tied to Owner
7540784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            UserInfo user = mgr.getUserInfo(mUserHandle);
755628ae0d84180c5f7c52725e02506021e532ed252Robin Lee            if (user.isRestricted()) {
756c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                throw new SecurityException("Restricted users cannot establish VPNs");
757c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
758c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
7594ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
7600784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                                                                        null, 0, mUserHandle);
7614ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (info == null) {
7624ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new SecurityException("Cannot find " + config.user);
7634ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            }
7644ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
7654ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
7664ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            }
7674ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } catch (RemoteException e) {
7684ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new SecurityException("Cannot find " + config.user);
7694ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } finally {
7704ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            Binder.restoreCallingIdentity(token);
771199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
772fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh
7734c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        // Save the old config in case we need to go back.
7744c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        VpnConfig oldConfig = mConfig;
7754c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        String oldInterface = mInterface;
7764c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        Connection oldConnection = mConnection;
7776bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        NetworkAgent oldNetworkAgent = mNetworkAgent;
7786bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkAgent = null;
7794d03abcd49af490dba3850d341b955dd72f24959Robin Lee        Set<UidRange> oldUsers = mVpnUsers;
7804c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker
781e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh        // Configure the interface. Abort if any of these steps fails.
78297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh        ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
783ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        try {
784899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            updateState(DetailedState.CONNECTING, "establish");
785c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            String interfaze = jniGetName(tun.getFd());
7864ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
787c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            // TEMP use the old jni calls until there is support for netd address setting
7884ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            StringBuilder builder = new StringBuilder();
7894ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            for (LinkAddress address : config.addresses) {
7904ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                builder.append(" " + address);
79197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            }
7924ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (jniSetAddresses(interfaze, builder.toString()) < 1) {
7934ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new IllegalArgumentException("At least one address must be specified");
79497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            }
795199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            Connection connection = new Connection();
796d69e4c1460017062e7c36be55801cb434ad19d97Dianne Hackborn            if (!mContext.bindServiceAsUser(intent, connection,
797d69e4c1460017062e7c36be55801cb434ad19d97Dianne Hackborn                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
798d69e4c1460017062e7c36be55801cb434ad19d97Dianne Hackborn                    new UserHandle(mUserHandle))) {
799199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                throw new IllegalStateException("Cannot bind " + config.user);
800199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
8014c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker
802199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mConnection = connection;
803c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            mInterface = interfaze;
8044ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
8054ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            // Fill more values.
8064ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            config.user = mPackage;
8074ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            config.interfaze = mInterface;
808c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            config.startTime = SystemClock.elapsedRealtime();
809c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            mConfig = config;
8104c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker
8114ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            // Set up forwarding and DNS rules.
8126bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentConnect();
8134ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
8144c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            if (oldConnection != null) {
8154c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker                mContext.unbindService(oldConnection);
8164c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            }
8176bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            // Remove the old tun's user forwarding rules
8186bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            // The new tun's user rules have already been added so they will take over
8196bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            // as rules are deleted. This prevents data leakage as the rules are moved over.
8206bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect(oldNetworkAgent);
8214c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            if (oldInterface != null && !oldInterface.equals(interfaze)) {
8224c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker                jniReset(oldInterface);
8234c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            }
8246bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson
8256bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson            try {
8266bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson                IoUtils.setBlocking(tun.getFileDescriptor(), config.blocking);
8276bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson            } catch (IOException e) {
8286bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson                throw new IllegalStateException(
8296bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson                        "Cannot set tunnel's fd as blocking=" + config.blocking, e);
8306bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson            }
831ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        } catch (RuntimeException e) {
832065b299df4159602327977dd007cb2cd6b64ab20Jeff Sharkey            IoUtils.closeQuietly(tun);
8336bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect();
8344c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            // restore old state
8354c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mConfig = oldConfig;
8364c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mConnection = oldConnection;
8374c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mVpnUsers = oldUsers;
8386bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent = oldNetworkAgent;
8394c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mInterface = oldInterface;
840ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            throw e;
841ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
842199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        Log.i(TAG, "Established by " + config.user + " on " + mInterface);
843c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        return tun;
844ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
845ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
846c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private boolean isRunningLocked() {
847c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        return mNetworkAgent != null && mInterface != null;
848c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    }
849c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran
850c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    // Returns true if the VPN has been established and the calling UID is its owner. Used to check
851c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    // that a call to mutate VPN state is admissible.
852c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    private boolean isCallerEstablishedOwnerLocked() {
853c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        return isRunningLocked() && Binder.getCallingUid() == mOwnerUID;
854c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
855c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
8560784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    // Note: Return type guarantees results are deduped and sorted, which callers require.
8570784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private SortedSet<Integer> getAppsUids(List<String> packageNames, int userHandle) {
8580784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        SortedSet<Integer> uids = new TreeSet<Integer>();
8590784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        for (String app : packageNames) {
8600784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            int uid = getAppUid(app, userHandle);
8610784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            if (uid != -1) uids.add(uid);
8620784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        }
8630784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        return uids;
8640784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    }
8650784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen
8664d03abcd49af490dba3850d341b955dd72f24959Robin Lee    /**
8674d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * Creates a {@link Set} of non-intersecting {@link UidRange} objects including all UIDs
8684d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * associated with one user, and any restricted profiles attached to that user.
8694d03abcd49af490dba3850d341b955dd72f24959Robin Lee     *
8704d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * <p>If one of {@param allowedApplications} or {@param disallowedApplications} is provided,
8714d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * the UID ranges will match the app whitelist or blacklist specified there. Otherwise, all UIDs
8724d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * in each user and profile will be included.
8734d03abcd49af490dba3850d341b955dd72f24959Robin Lee     *
8744d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @param userHandle The userId to create UID ranges for along with any of its restricted
8754d03abcd49af490dba3850d341b955dd72f24959Robin Lee     *                   profiles.
8764d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @param allowedApplications (optional) whitelist of applications to include.
8774d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @param disallowedApplications (optional) blacklist of applications to exclude.
8784d03abcd49af490dba3850d341b955dd72f24959Robin Lee     */
8794d03abcd49af490dba3850d341b955dd72f24959Robin Lee    @VisibleForTesting
8804d03abcd49af490dba3850d341b955dd72f24959Robin Lee    Set<UidRange> createUserAndRestrictedProfilesRanges(@UserIdInt int userHandle,
8814d03abcd49af490dba3850d341b955dd72f24959Robin Lee            @Nullable List<String> allowedApplications,
8824d03abcd49af490dba3850d341b955dd72f24959Robin Lee            @Nullable List<String> disallowedApplications) {
8834d03abcd49af490dba3850d341b955dd72f24959Robin Lee        final Set<UidRange> ranges = new ArraySet<>();
8844d03abcd49af490dba3850d341b955dd72f24959Robin Lee
8854d03abcd49af490dba3850d341b955dd72f24959Robin Lee        // Assign the top-level user to the set of ranges
8864d03abcd49af490dba3850d341b955dd72f24959Robin Lee        addUserToRanges(ranges, userHandle, allowedApplications, disallowedApplications);
8874d03abcd49af490dba3850d341b955dd72f24959Robin Lee
8884d03abcd49af490dba3850d341b955dd72f24959Robin Lee        // If the user can have restricted profiles, assign all its restricted profiles too
8894d03abcd49af490dba3850d341b955dd72f24959Robin Lee        if (canHaveRestrictedProfile(userHandle)) {
8904d03abcd49af490dba3850d341b955dd72f24959Robin Lee            final long token = Binder.clearCallingIdentity();
8914d03abcd49af490dba3850d341b955dd72f24959Robin Lee            List<UserInfo> users;
8924d03abcd49af490dba3850d341b955dd72f24959Robin Lee            try {
89317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                users = UserManager.get(mContext).getUsers(true);
8944d03abcd49af490dba3850d341b955dd72f24959Robin Lee            } finally {
8954d03abcd49af490dba3850d341b955dd72f24959Robin Lee                Binder.restoreCallingIdentity(token);
8964d03abcd49af490dba3850d341b955dd72f24959Robin Lee            }
8974d03abcd49af490dba3850d341b955dd72f24959Robin Lee            for (UserInfo user : users) {
8984d03abcd49af490dba3850d341b955dd72f24959Robin Lee                if (user.isRestricted() && (user.restrictedProfileParentId == userHandle)) {
8994d03abcd49af490dba3850d341b955dd72f24959Robin Lee                    addUserToRanges(ranges, user.id, allowedApplications, disallowedApplications);
9004d03abcd49af490dba3850d341b955dd72f24959Robin Lee                }
9014d03abcd49af490dba3850d341b955dd72f24959Robin Lee            }
902c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
9034d03abcd49af490dba3850d341b955dd72f24959Robin Lee        return ranges;
9044d03abcd49af490dba3850d341b955dd72f24959Robin Lee    }
90569887e838814642a7ae78fc810656c7c8afc2a19Robert Greenwalt
9064d03abcd49af490dba3850d341b955dd72f24959Robin Lee    /**
9074d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * Updates a {@link Set} of non-intersecting {@link UidRange} objects to include all UIDs
9084d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * associated with one user.
9094d03abcd49af490dba3850d341b955dd72f24959Robin Lee     *
9104d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * <p>If one of {@param allowedApplications} or {@param disallowedApplications} is provided,
9114d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * the UID ranges will match the app whitelist or blacklist specified there. Otherwise, all UIDs
9124d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * in the user will be included.
9134d03abcd49af490dba3850d341b955dd72f24959Robin Lee     *
9144d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @param ranges {@link Set} of {@link UidRange}s to which to add.
9154d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @param userHandle The userId to add to {@param ranges}.
9164d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @param allowedApplications (optional) whitelist of applications to include.
9174d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @param disallowedApplications (optional) blacklist of applications to exclude.
9184d03abcd49af490dba3850d341b955dd72f24959Robin Lee     */
9194d03abcd49af490dba3850d341b955dd72f24959Robin Lee    @VisibleForTesting
9204d03abcd49af490dba3850d341b955dd72f24959Robin Lee    void addUserToRanges(@NonNull Set<UidRange> ranges, @UserIdInt int userHandle,
9214d03abcd49af490dba3850d341b955dd72f24959Robin Lee            @Nullable List<String> allowedApplications,
9224d03abcd49af490dba3850d341b955dd72f24959Robin Lee            @Nullable List<String> disallowedApplications) {
9234d03abcd49af490dba3850d341b955dd72f24959Robin Lee        if (allowedApplications != null) {
9240784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            // Add ranges covering all UIDs for allowedApplications.
9250784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            int start = -1, stop = -1;
9264d03abcd49af490dba3850d341b955dd72f24959Robin Lee            for (int uid : getAppsUids(allowedApplications, userHandle)) {
9270784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                if (start == -1) {
9280784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    start = uid;
9290784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                } else if (uid != stop + 1) {
9304d03abcd49af490dba3850d341b955dd72f24959Robin Lee                    ranges.add(new UidRange(start, stop));
9310784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    start = uid;
9320784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                }
9330784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                stop = uid;
9340784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            }
9354d03abcd49af490dba3850d341b955dd72f24959Robin Lee            if (start != -1) ranges.add(new UidRange(start, stop));
9364d03abcd49af490dba3850d341b955dd72f24959Robin Lee        } else if (disallowedApplications != null) {
9370784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            // Add all ranges for user skipping UIDs for disallowedApplications.
9380784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            final UidRange userRange = UidRange.createForUser(userHandle);
9390784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            int start = userRange.start;
9404d03abcd49af490dba3850d341b955dd72f24959Robin Lee            for (int uid : getAppsUids(disallowedApplications, userHandle)) {
9410784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                if (uid == start) {
9420784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    start++;
9430784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                } else {
9444d03abcd49af490dba3850d341b955dd72f24959Robin Lee                    ranges.add(new UidRange(start, uid - 1));
9450784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    start = uid + 1;
9460784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                }
9470784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            }
9484d03abcd49af490dba3850d341b955dd72f24959Robin Lee            if (start <= userRange.stop) ranges.add(new UidRange(start, userRange.stop));
9490784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        } else {
9500784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            // Add all UIDs for the user.
9514d03abcd49af490dba3850d341b955dd72f24959Robin Lee            ranges.add(UidRange.createForUser(userHandle));
9520784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        }
953c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
954c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
9550784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    // Returns the subset of the full list of active UID ranges the VPN applies to (mVpnUsers) that
9560784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    // apply to userHandle.
9570784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private List<UidRange> uidRangesForUser(int userHandle) {
9580784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        final UidRange userRange = UidRange.createForUser(userHandle);
9590784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        final List<UidRange> ranges = new ArrayList<UidRange>();
9600784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        for (UidRange range : mVpnUsers) {
9614d03abcd49af490dba3850d341b955dd72f24959Robin Lee            if (userRange.containsRange(range)) {
9620784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                ranges.add(range);
9630784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            }
9640784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        }
9650784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        return ranges;
9660784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    }
9670784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen
9680784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private void removeVpnUserLocked(int userHandle) {
969c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (mVpnUsers == null) {
97090b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            throw new IllegalStateException("VPN is not active");
97190b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        }
9720784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        final List<UidRange> ranges = uidRangesForUser(userHandle);
97390b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        if (mNetworkAgent != null) {
9740784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            mNetworkAgent.removeUidRanges(ranges.toArray(new UidRange[ranges.size()]));
97590b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        }
9760784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        mVpnUsers.removeAll(ranges);
977c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
978c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
9791c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov    public void onUserAdded(int userHandle) {
9801c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov        // If the user is restricted tie them to the parent user's VPN
9811c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov        UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
98217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
9831c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov            synchronized(Vpn.this) {
98417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                if (mVpnUsers != null) {
98517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    try {
98617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                        addUserToRanges(mVpnUsers, userHandle, mConfig.allowedApplications,
98717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                                mConfig.disallowedApplications);
98817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                        if (mNetworkAgent != null) {
98917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                            final List<UidRange> ranges = uidRangesForUser(userHandle);
99017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                            mNetworkAgent.addUidRanges(ranges.toArray(new UidRange[ranges.size()]));
99117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                        }
99217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    } catch (Exception e) {
99317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                        Log.wtf(TAG, "Failed to add restricted user to owner", e);
9946bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    }
99517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                }
99617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                if (mAlwaysOn) {
99717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    setVpnForcedLocked(mLockdown);
998c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                }
999c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
1000c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
1001c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
1002c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
10031c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov    public void onUserRemoved(int userHandle) {
1004c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        // clean up if restricted
10051c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov        UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
100617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
10071c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov            synchronized(Vpn.this) {
100817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                if (mVpnUsers != null) {
100917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    try {
101017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                        removeVpnUserLocked(userHandle);
101117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    } catch (Exception e) {
101217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                        Log.wtf(TAG, "Failed to remove restricted user to owner", e);
101317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    }
101417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                }
101517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                if (mAlwaysOn) {
101617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    setVpnForcedLocked(mLockdown);
1017c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                }
1018c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
1019c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
1020c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
1021c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
1022bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker    /**
102317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * Called when the user associated with this VPN has just been stopped.
102417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     */
102517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    public synchronized void onUserStopped() {
102617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        // Switch off networking lockdown (if it was enabled)
102717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        setVpnForcedLocked(false);
102817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        mAlwaysOn = false;
102917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
1030812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        unregisterPackageChangeReceiverLocked();
103117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        // Quit any active connections
103217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        agentDisconnect();
103317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    }
103417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
103517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    /**
103617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * Restrict network access from all UIDs affected by this {@link Vpn}, apart from the VPN
103717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * service app itself, to only sockets that have had {@code protect()} called on them. All
103817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * non-VPN traffic is blocked via a {@code PROHIBIT} response from the kernel.
103917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *
104017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * The exception for the VPN UID isn't technically necessary -- setup should use protected
104117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * sockets -- but in practice it saves apps that don't protect their sockets from breaking.
104217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *
104317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * Calling multiple times with {@param enforce} = {@code true} will recreate the set of UIDs to
104417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * block every time, and if anything has changed update using {@link #setAllowOnlyVpnForUids}.
104517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *
104617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @param enforce {@code true} to require that all traffic under the jurisdiction of this
104717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *                {@link Vpn} goes through a VPN connection or is blocked until one is
104817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *                available, {@code false} to lift the requirement.
104917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *
105017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @see #mBlockedUsers
105117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     */
105217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    @GuardedBy("this")
105317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    private void setVpnForcedLocked(boolean enforce) {
105417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        final Set<UidRange> removedRanges = new ArraySet<>(mBlockedUsers);
105517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        if (enforce) {
105617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            final Set<UidRange> addedRanges = createUserAndRestrictedProfilesRanges(mUserHandle,
105717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    /* allowedApplications */ null,
105817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    /* disallowedApplications */ Collections.singletonList(mPackage));
105917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
106017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            removedRanges.removeAll(addedRanges);
106117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            addedRanges.removeAll(mBlockedUsers);
106217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
106317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            setAllowOnlyVpnForUids(false, removedRanges);
106417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            setAllowOnlyVpnForUids(true, addedRanges);
106517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        } else {
106617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            setAllowOnlyVpnForUids(false, removedRanges);
106717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        }
106817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    }
106917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
107017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    /**
107117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * Either add or remove a list of {@link UidRange}s to the list of UIDs that are only allowed
107217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * to make connections through sockets that have had {@code protect()} called on them.
107317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *
107417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @param enforce {@code true} to add to the blacklist, {@code false} to remove.
107517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @param ranges {@link Collection} of {@link UidRange}s to add (if {@param enforce} is
107617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *               {@code true}) or to remove.
107717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @return {@code true} if all of the UIDs were added/removed. {@code false} otherwise,
107817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *         including added ranges that already existed or removed ones that didn't.
107917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     */
108017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    @GuardedBy("this")
108117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    private boolean setAllowOnlyVpnForUids(boolean enforce, Collection<UidRange> ranges) {
108217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        if (ranges.size() == 0) {
108317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            return true;
108417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        }
108517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        final UidRange[] rangesArray = ranges.toArray(new UidRange[ranges.size()]);
108617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        try {
108717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            mNetd.setAllowOnlyVpnForUids(enforce, rangesArray);
108817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        } catch (RemoteException | RuntimeException e) {
108917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            Log.e(TAG, "Updating blocked=" + enforce
109017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    + " for UIDs " + Arrays.toString(ranges.toArray()) + " failed", e);
109117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            return false;
109217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        }
109317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        if (enforce) {
109417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            mBlockedUsers.addAll(ranges);
109517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        } else {
109617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            mBlockedUsers.removeAll(ranges);
109717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        }
109817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        return true;
109917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    }
110017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
110117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    /**
1102bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker     * Return the configuration of the currently running VPN.
1103bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker     */
1104bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker    public VpnConfig getVpnConfig() {
1105bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker        enforceControlPermission();
1106bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker        return mConfig;
1107bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker    }
1108bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker
1109899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    @Deprecated
1110899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    public synchronized void interfaceStatusChanged(String iface, boolean up) {
1111899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        try {
1112899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            mObserver.interfaceStatusChanged(iface, up);
1113899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        } catch (RemoteException e) {
1114899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            // ignored; target is local
1115aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        }
1116ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
1117ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
1118899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
1119899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        @Override
1120899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        public void interfaceStatusChanged(String interfaze, boolean up) {
1121899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            synchronized (Vpn.this) {
1122899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                if (!up && mLegacyVpnRunner != null) {
1123899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    mLegacyVpnRunner.check(interfaze);
1124899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
1125199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
1126ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
1127ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
1128899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        @Override
1129899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        public void interfaceRemoved(String interfaze) {
1130899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            synchronized (Vpn.this) {
1131899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
113290b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson                    mStatusIntent = null;
11336bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    mVpnUsers = null;
1134c4c7231eb6d1efa9ecd7b693f8328a76a04e8bbbPaul Jensen                    mConfig = null;
1135899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    mInterface = null;
1136899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    if (mConnection != null) {
1137899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mContext.unbindService(mConnection);
1138899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mConnection = null;
11396bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        agentDisconnect();
1140899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    } else if (mLegacyVpnRunner != null) {
1141899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mLegacyVpnRunner.exit();
1142899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mLegacyVpnRunner = null;
1143899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    }
1144899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
1145899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
1146899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
1147899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    };
1148db3c8678e5cbdfec011afaf25bde2091152c30adHaoyu Bai
1149dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh    private void enforceControlPermission() {
1150bc19c181c8c058c824e4fee907a05129e142c388Jeff Davidson        mContext.enforceCallingPermission(Manifest.permission.CONTROL_VPN, "Unauthorized Caller");
1151dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh    }
1152dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh
1153244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee    private void enforceControlPermissionOrInternalCaller() {
1154244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        // Require caller to be either an application with CONTROL_VPN permission or a process
1155244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        // in the system server.
1156244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        mContext.enforceCallingOrSelfPermission(Manifest.permission.CONTROL_VPN,
1157244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee                "Unauthorized Caller");
1158244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee    }
1159244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee
1160199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    private class Connection implements ServiceConnection {
1161199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        private IBinder mService;
1162199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
1163199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        @Override
1164199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        public void onServiceConnected(ComponentName name, IBinder service) {
1165199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mService = service;
1166199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
1167199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
1168199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        @Override
1169199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        public void onServiceDisconnected(ComponentName name) {
1170199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mService = null;
1171199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
1172199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    }
1173199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
117490b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson    private void prepareStatusIntent() {
117590b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        final long token = Binder.clearCallingIdentity();
117690b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        try {
117790b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
117890b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        } finally {
117990b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            Binder.restoreCallingIdentity(token);
118090b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        }
118190b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson    }
118290b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson
1183f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    public synchronized boolean addAddress(String address, int prefixLength) {
1184c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (!isCallerEstablishedOwnerLocked()) {
1185f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran            return false;
1186f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        }
1187f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        boolean success = jniAddAddress(mInterface, address, prefixLength);
1188c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        mNetworkAgent.sendLinkProperties(makeLinkProperties());
1189f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        return success;
1190f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    }
1191f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran
1192f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    public synchronized boolean removeAddress(String address, int prefixLength) {
1193c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (!isCallerEstablishedOwnerLocked()) {
1194f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran            return false;
1195f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        }
1196f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        boolean success = jniDelAddress(mInterface, address, prefixLength);
1197c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        mNetworkAgent.sendLinkProperties(makeLinkProperties());
1198f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        return success;
1199f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    }
1200f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran
1201c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    public synchronized boolean setUnderlyingNetworks(Network[] networks) {
1202c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (!isCallerEstablishedOwnerLocked()) {
1203c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            return false;
1204c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        }
1205c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (networks == null) {
1206c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            mConfig.underlyingNetworks = null;
1207c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        } else {
1208c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            mConfig.underlyingNetworks = new Network[networks.length];
1209c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            for (int i = 0; i < networks.length; ++i) {
1210c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                if (networks[i] == null) {
1211c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                    mConfig.underlyingNetworks[i] = null;
1212c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                } else {
1213c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                    mConfig.underlyingNetworks[i] = new Network(networks[i].netId);
1214c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                }
1215c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            }
1216c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        }
1217c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        return true;
1218c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    }
1219c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran
1220c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    public synchronized Network[] getUnderlyingNetworks() {
1221c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (!isRunningLocked()) {
1222c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            return null;
1223c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        }
1224c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        return mConfig.underlyingNetworks;
1225c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    }
1226c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran
1227f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong    /**
1228f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong     * This method should only be called by ConnectivityService. Because it doesn't
1229f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong     * have enough data to fill VpnInfo.primaryUnderlyingIface field.
1230f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong     */
1231f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong    public synchronized VpnInfo getVpnInfo() {
1232f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        if (!isRunningLocked()) {
1233f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong            return null;
1234f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        }
1235f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong
1236f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        VpnInfo info = new VpnInfo();
1237f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        info.ownerUid = mOwnerUID;
1238f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        info.vpnIface = mInterface;
1239f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        return info;
1240f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong    }
1241f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong
1242c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    public synchronized boolean appliesToUid(int uid) {
1243c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (!isRunningLocked()) {
1244c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            return false;
1245c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        }
1246c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        for (UidRange uidRange : mVpnUsers) {
12474d03abcd49af490dba3850d341b955dd72f24959Robin Lee            if (uidRange.contains(uid)) {
1248c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                return true;
1249c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            }
1250c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        }
1251c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        return false;
1252c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    }
1253c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran
125417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    /**
1255ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee     * @return {@code true} if {@param uid} is blocked by an always-on VPN.
1256ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee     *         A UID is blocked if it's included in one of the mBlockedUsers ranges and the VPN is
1257ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee     *         not connected, or if the VPN is connected but does not apply to the UID.
125817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *
125917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @see #mBlockedUsers
126017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     */
126117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    public synchronized boolean isBlockingUid(int uid) {
1262ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee        if (!mLockdown) {
1263ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee            return false;
1264ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee        }
1265ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee
1266ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee        if (mNetworkInfo.isConnected()) {
1267ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee            return !appliesToUid(uid);
1268ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee        } else {
1269ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee            for (UidRange uidRange : mBlockedUsers) {
1270ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee                if (uidRange.contains(uid)) {
1271ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee                    return true;
1272ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee                }
127317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            }
1274ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee            return false;
127517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        }
127617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    }
127717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
12781a405fe300950d6ceae2166fd074b596d8110dbeTony Mak    private void updateAlwaysOnNotification(DetailedState networkState) {
12791a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        final boolean visible = (mAlwaysOn && networkState != DetailedState.CONNECTED);
12801a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        updateAlwaysOnNotificationInternal(visible);
12811a405fe300950d6ceae2166fd074b596d8110dbeTony Mak    }
12821a405fe300950d6ceae2166fd074b596d8110dbeTony Mak
12831a405fe300950d6ceae2166fd074b596d8110dbeTony Mak    @VisibleForTesting
12841a405fe300950d6ceae2166fd074b596d8110dbeTony Mak    protected void updateAlwaysOnNotificationInternal(boolean visible) {
12851a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        final UserHandle user = UserHandle.of(mUserHandle);
12861a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        final long token = Binder.clearCallingIdentity();
12871a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        try {
12881a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            final NotificationManager notificationManager = NotificationManager.from(mContext);
12891a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            if (!visible) {
129015297a67d3240157906a3fb3ed028043d7454d1aCharles He                notificationManager.cancelAsUser(TAG, SystemMessage.NOTE_VPN_DISCONNECTED, user);
12911a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                return;
12921a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            }
12931a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS);
12941a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            final PendingIntent configIntent = PendingIntent.getActivityAsUser(
12951a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                    mContext, /* request */ 0, intent,
12961a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                    PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
12971a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                    null, user);
1298af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch            final Notification.Builder builder =
1299af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                    new Notification.Builder(mContext, SystemNotificationChannels.VPN)
1300af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                            .setSmallIcon(R.drawable.vpn_connected)
1301af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                            .setContentTitle(mContext.getString(R.string.vpn_lockdown_disconnected))
1302af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                            .setContentText(mContext.getString(R.string.vpn_lockdown_config))
1303af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                            .setContentIntent(configIntent)
1304af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                            .setCategory(Notification.CATEGORY_SYSTEM)
1305af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                            .setVisibility(Notification.VISIBILITY_PUBLIC)
1306af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                            .setOngoing(true)
1307af759c52ce01fe6b5144957e38da956af01a217bGeoffrey Pitsch                            .setColor(mContext.getColor(R.color.system_notification_accent_color));
1308282cfefea0fbbd299839e353e6d30affdcd4a55cChris Wren            notificationManager.notifyAsUser(TAG, SystemMessage.NOTE_VPN_DISCONNECTED,
1309282cfefea0fbbd299839e353e6d30affdcd4a55cChris Wren                    builder.build(), user);
13101a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        } finally {
13111a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            Binder.restoreCallingIdentity(token);
13121a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        }
13131a405fe300950d6ceae2166fd074b596d8110dbeTony Mak    }
13141a405fe300950d6ceae2166fd074b596d8110dbeTony Mak
131597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh    private native int jniCreate(int mtu);
1316c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native String jniGetName(int tun);
131797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh    private native int jniSetAddresses(String interfaze, String addresses);
1318c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native void jniReset(String interfaze);
1319c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native int jniCheck(String interfaze);
1320f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    private native boolean jniAddAddress(String interfaze, String address, int prefixLen);
1321f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    private native boolean jniDelAddress(String interfaze, String address, int prefixLen);
132285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
132341fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti    private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
132441fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        for (RouteInfo route : prop.getAllRoutes()) {
132582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            // Currently legacy VPN only works on IPv4.
132682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
132741fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti                return route;
132882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            }
132982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
133082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
133141fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        throw new IllegalStateException("Unable to find IPv4 default gateway");
133282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    }
133382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
133485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    /**
133582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey     * Start legacy VPN, controlling native daemons as needed. Creates a
133682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey     * secondary thread to perform connection work, returning quickly.
1337b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     *
1338b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * Should only be called to respond to Binder requests as this enforces caller permission. Use
1339b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, LinkProperties)} to skip the
1340b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * permission check only when the caller is trusted (or the call is initiated by the system).
134185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     */
134282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
13435a6bdc46e2fdc8cfd930396773dd89efd19fa1f1Robert Greenwalt        enforceControlPermission();
1344b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        long token = Binder.clearCallingIdentity();
1345b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        try {
1346b21298a686b04d55ff97223dd317497845713f4bJeff Davidson            startLegacyVpnPrivileged(profile, keyStore, egress);
1347b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        } finally {
1348b21298a686b04d55ff97223dd317497845713f4bJeff Davidson            Binder.restoreCallingIdentity(token);
1349b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        }
1350b21298a686b04d55ff97223dd317497845713f4bJeff Davidson    }
1351b21298a686b04d55ff97223dd317497845713f4bJeff Davidson
1352b21298a686b04d55ff97223dd317497845713f4bJeff Davidson    /**
1353b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * Like {@link #startLegacyVpn(VpnProfile, KeyStore, LinkProperties)}, but does not check
1354b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * permissions under the assumption that the caller is the system.
1355b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     *
1356b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * Callers are responsible for checking permissions if needed.
1357b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     */
1358b21298a686b04d55ff97223dd317497845713f4bJeff Davidson    public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
1359b21298a686b04d55ff97223dd317497845713f4bJeff Davidson            LinkProperties egress) {
1360f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds        UserManager mgr = UserManager.get(mContext);
13610784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        UserInfo user = mgr.getUserInfo(mUserHandle);
136295778ffc58979d19ff9f4aaed396a6eca49cf698Nicolas Prevot        if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
136395778ffc58979d19ff9f4aaed396a6eca49cf698Nicolas Prevot                    new UserHandle(mUserHandle))) {
1364f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds            throw new SecurityException("Restricted users cannot establish VPNs");
1365f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds        }
136682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
136741fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
136841fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
136941fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        final String iface = ipv4DefaultRoute.getInterface();
137082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
137182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        // Load certificates.
137282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String privateKey = "";
137382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String userCert = "";
137482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String caCert = "";
137582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String serverCert = "";
137682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.ipsecUserCert.isEmpty()) {
137782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
137882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
1379d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes            userCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
138082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
138182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.ipsecCaCert.isEmpty()) {
138282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
1383d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes            caCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
138482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
138582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.ipsecServerCert.isEmpty()) {
138682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
1387d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes            serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
138882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
138982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
139082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            throw new IllegalStateException("Cannot load credentials");
139182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
139282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
139382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        // Prepare arguments for racoon.
139482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String[] racoon = null;
139582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        switch (profile.type) {
139682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
139782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
139882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "udppsk", profile.ipsecIdentifier,
139982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    profile.ipsecSecret, "1701",
140082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
140182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
140282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
140382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
140482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "udprsa", privateKey, userCert,
140582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    caCert, serverCert, "1701",
140682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
140782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
140882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
140982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
141082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
141182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    profile.ipsecSecret, profile.username, profile.password, "", gateway,
141282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
141382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
141482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
141582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
141682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "xauthrsa", privateKey, userCert,
141782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    caCert, serverCert, profile.username, profile.password, "", gateway,
141882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
141982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
142082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
142182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
142282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "hybridrsa",
142382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    caCert, serverCert, profile.username, profile.password, "", gateway,
142482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
142582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
142682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
142782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
142882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        // Prepare arguments for mtpd.
142982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String[] mtpd = null;
143082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        switch (profile.type) {
143182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_PPTP:
143282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                mtpd = new String[] {
143382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, "pptp", profile.server, "1723",
143482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "name", profile.username, "password", profile.password,
143582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
143682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
143782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    (profile.mppe ? "+mppe" : "nomppe"),
143882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
143982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
144082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
144182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
144282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                mtpd = new String[] {
144382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
144482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "name", profile.username, "password", profile.password,
144582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
144682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
144782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
144882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
144982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
1450899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
145182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        VpnConfig config = new VpnConfig();
1452899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        config.legacy = true;
145382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        config.user = profile.key;
145482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        config.interfaze = iface;
145582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        config.session = profile.name;
14564ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
14574ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        config.addLegacyRoutes(profile.routes);
145882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.dnsServers.isEmpty()) {
145982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
146082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
146182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.searchDomains.isEmpty()) {
146282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
146382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
146482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        startLegacyVpn(config, racoon, mtpd);
146582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    }
146682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
146782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
1468b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        stopLegacyVpnPrivileged();
1469899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1470b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        // Prepare for the new request.
1471b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        prepareInternal(VpnConfig.LEGACY_VPN);
1472899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        updateState(DetailedState.CONNECTING, "startLegacyVpn");
147385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
14742e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        // Start a new LegacyVpnRunner and we are done!
1475100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
1476100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        mLegacyVpnRunner.start();
147785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    }
147885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
1479b21298a686b04d55ff97223dd317497845713f4bJeff Davidson    /** Stop legacy VPN. Permissions must be checked by callers. */
1480b21298a686b04d55ff97223dd317497845713f4bJeff Davidson    public synchronized void stopLegacyVpnPrivileged() {
1481899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        if (mLegacyVpnRunner != null) {
1482899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            mLegacyVpnRunner.exit();
1483899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            mLegacyVpnRunner = null;
1484899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1485899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            synchronized (LegacyVpnRunner.TAG) {
1486899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                // wait for old thread to completely finish before spinning up
1487899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                // new instance, otherwise state updates can be out of order.
1488899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
1489899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
1490899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    }
1491899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
149285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    /**
14932e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     * Return the information of the current ongoing legacy VPN.
14942e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     */
14952e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    public synchronized LegacyVpnInfo getLegacyVpnInfo() {
1496dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        // Check if the caller is authorized.
1497dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        enforceControlPermission();
149808bbca040fa921b99493cd9967453ed90b1b710asj.cha        return getLegacyVpnInfoPrivileged();
149908bbca040fa921b99493cd9967453ed90b1b710asj.cha    }
150008bbca040fa921b99493cd9967453ed90b1b710asj.cha
150108bbca040fa921b99493cd9967453ed90b1b710asj.cha    /**
150208bbca040fa921b99493cd9967453ed90b1b710asj.cha     * Return the information of the current ongoing legacy VPN.
150308bbca040fa921b99493cd9967453ed90b1b710asj.cha     * Callers are responsible for checking permissions if needed.
150408bbca040fa921b99493cd9967453ed90b1b710asj.cha     */
150508bbca040fa921b99493cd9967453ed90b1b710asj.cha    public synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() {
1506899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        if (mLegacyVpnRunner == null) return null;
1507899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1508899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        final LegacyVpnInfo info = new LegacyVpnInfo();
1509c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        info.key = mConfig.user;
1510899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
151190b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        if (mNetworkInfo.isConnected()) {
151290b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            info.intent = mStatusIntent;
151390b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        }
1514899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        return info;
15152e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    }
15162e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
151769ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public VpnConfig getLegacyVpnConfig() {
151869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        if (mLegacyVpnRunner != null) {
1519c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            return mConfig;
152069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        } else {
152169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            return null;
152269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
152369ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
152469ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
15252e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    /**
152685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * Bringing up a VPN connection takes time, and that is all this thread
152785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * does. Here we have plenty of time. The only thing we need to take
152885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * care of is responding to interruptions as soon as possible. Otherwise
152985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * requests will be piled up. This can be done in a Handler as a state
153085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * machine, but it is much easier to read in the current form.
153185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     */
153285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    private class LegacyVpnRunner extends Thread {
153385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private static final String TAG = "LegacyVpnRunner";
153485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
15351f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh        private final String[] mDaemons;
153685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private final String[][] mArguments;
15375317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh        private final LocalSocket[] mSockets;
153853c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt        private final String mOuterInterface;
15391b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        private final AtomicInteger mOuterConnection =
15401b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                new AtomicInteger(ConnectivityManager.TYPE_NONE);
15412e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
154285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private long mTimer = -1;
154385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
15441b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        /**
15451b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt         * Watch for the outer connection (passing in the constructor) going away.
15461b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt         */
15471b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
15481b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            @Override
15491b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            public void onReceive(Context context, Intent intent) {
155057666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey                if (!mEnableTeardown) return;
155157666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey
15521b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
15531b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                    if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
15541b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                            ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
15551b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        NetworkInfo info = (NetworkInfo)intent.getExtra(
15561b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                                ConnectivityManager.EXTRA_NETWORK_INFO);
15571b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        if (info != null && !info.isConnectedOrConnecting()) {
15581b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                            try {
15591b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                                mObserver.interfaceStatusChanged(mOuterInterface, false);
15601b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                            } catch (RemoteException e) {}
15611b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        }
15621b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                    }
15631b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                }
15641b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            }
15651b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        };
15661b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt
156741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
156885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            super(TAG);
156941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mConfig = config;
157041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mDaemons = new String[] {"racoon", "mtpd"};
1571899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            // TODO: clear arguments from memory once launched
157241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mArguments = new String[][] {racoon, mtpd};
15735317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh            mSockets = new LocalSocket[mDaemons.length];
157453c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt
157553c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            // This is the interface which VPN is running on,
157653c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            // mConfig.interfaze will change to point to OUR
157753c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            // internal interface soon. TODO - add inner/outer to mconfig
15781b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
15794ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            // we will leave the VPN up.  We should check that it's still there/connected after
15801b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            // registering
158153c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            mOuterInterface = mConfig.interfaze;
15821b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt
1583e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen            if (!TextUtils.isEmpty(mOuterInterface)) {
1584e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                final ConnectivityManager cm = ConnectivityManager.from(mContext);
1585e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                for (Network network : cm.getAllNetworks()) {
1586e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                    final LinkProperties lp = cm.getLinkProperties(network);
15871b60d11b8f54f1ade45b80668601bc955041cf4fLorenzo Colitti                    if (lp != null && lp.getAllInterfaceNames().contains(mOuterInterface)) {
1588e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                        final NetworkInfo networkInfo = cm.getNetworkInfo(network);
1589e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                        if (networkInfo != null) mOuterConnection.set(networkInfo.getType());
1590e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                    }
1591e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                }
15921b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            }
15931b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt
15941b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            IntentFilter filter = new IntentFilter();
15951b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
15961b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            mContext.registerReceiver(mBroadcastReceiver, filter);
159741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        }
159841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
1599aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        public void check(String interfaze) {
160053c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            if (interfaze.equals(mOuterInterface)) {
1601aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh                Log.i(TAG, "Legacy VPN is going down with " + interfaze);
1602aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh                exit();
1603aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh            }
1604aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        }
1605aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh
160641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        public void exit() {
16075317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh            // We assume that everything is reset after stopping the daemons.
160897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            interrupt();
16096bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect();
16101b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            try {
16111b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                mContext.unregisterReceiver(mBroadcastReceiver);
16121b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            } catch (IllegalArgumentException e) {}
16132e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        }
16142e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
161585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        @Override
161685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        public void run() {
161785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            // Wait for the previous thread since it has been interrupted.
16182e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            Log.v(TAG, "Waiting");
161985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            synchronized (TAG) {
16202e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                Log.v(TAG, "Executing");
1621047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                try {
1622047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    execute();
1623047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    monitorDaemons();
1624047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    interrupted(); // Clear interrupt flag if execute called exit.
1625047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                } catch (InterruptedException e) {
1626047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                } finally {
1627047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    for (LocalSocket socket : mSockets) {
1628047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                        IoUtils.closeQuietly(socket);
1629047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    }
1630047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    // This sleep is necessary for racoon to successfully complete sending delete
1631047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    // message to server.
1632047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    try {
1633047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                        Thread.sleep(50);
1634047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    } catch (InterruptedException e) {
1635047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    }
1636047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    for (String daemon : mDaemons) {
1637047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                        SystemService.stop(daemon);
1638047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    }
1639047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                }
1640047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                agentDisconnect();
164185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
164285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
164385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
164485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private void checkpoint(boolean yield) throws InterruptedException {
164585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            long now = SystemClock.elapsedRealtime();
164685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            if (mTimer == -1) {
164785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                mTimer = now;
164885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Thread.sleep(1);
16497ef8611b5f3a893a46c7b9e22bdd8ab252e373ffChia-chi Yeh            } else if (now - mTimer <= 60000) {
165085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Thread.sleep(yield ? 200 : 1);
165185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } else {
1652899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                updateState(DetailedState.FAILED, "checkpoint");
165397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                throw new IllegalStateException("Time is up");
165485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
165585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
165685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
165785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private void execute() {
165885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            // Catch all exceptions so we can clean up few things.
1659899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            boolean initFinished = false;
166085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            try {
166185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // Initialize the timer.
166285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                checkpoint(false);
166385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
16641f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                // Wait for the daemons to stop.
16651f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                for (String daemon : mDaemons) {
1666088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                    while (!SystemService.isStopped(daemon)) {
166785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
166885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
166985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
167085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
167197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Clear the previous state.
167297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                File state = new File("/data/misc/vpn/state");
167397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                state.delete();
167497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (state.exists()) {
167597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    throw new IllegalStateException("Cannot delete the state");
167685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
1677c1872732922214de80f790e14865e41dd1b98203Chia-chi Yeh                new File("/data/misc/vpn/abort").delete();
1678899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                initFinished = true;
167985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
1680e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                // Check if we need to restart any of the daemons.
168185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                boolean restart = false;
168285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                for (String[] arguments : mArguments) {
168385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    restart = restart || (arguments != null);
168485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
168585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                if (!restart) {
16866bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    agentDisconnect();
168785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    return;
168885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
1689899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                updateState(DetailedState.CONNECTING, "execute");
169085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
16911f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                // Start the daemon with arguments.
16921f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                for (int i = 0; i < mDaemons.length; ++i) {
169385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    String[] arguments = mArguments[i];
169485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    if (arguments == null) {
169585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        continue;
169685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
169785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
16981f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Start the daemon.
16991f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    String daemon = mDaemons[i];
1700088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                    SystemService.start(daemon);
170185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
17021f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Wait for the daemon to start.
1703088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                    while (!SystemService.isRunning(daemon)) {
170485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
170585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
170685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
170785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Create the control socket.
17085317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    mSockets[i] = new LocalSocket();
170985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    LocalSocketAddress address = new LocalSocketAddress(
17101f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                            daemon, LocalSocketAddress.Namespace.RESERVED);
171185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
171285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Wait for the socket to connect.
171385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    while (true) {
171485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        try {
17155317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                            mSockets[i].connect(address);
171685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            break;
171785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        } catch (Exception e) {
171885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            // ignore
171985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
172085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
172185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
17225317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    mSockets[i].setSoTimeout(500);
172385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
172485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Send over the arguments.
17255317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    OutputStream out = mSockets[i].getOutputStream();
172685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    for (String argument : arguments) {
1727d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes                        byte[] bytes = argument.getBytes(StandardCharsets.UTF_8);
17285317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                        if (bytes.length >= 0xFFFF) {
172997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            throw new IllegalArgumentException("Argument is too large");
173085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
17311f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes.length >> 8);
17321f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes.length);
17331f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes);
173485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(false);
173585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
17365317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    out.write(0xFF);
17375317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    out.write(0xFF);
17381f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    out.flush();
173997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
174097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    // Wait for End-of-File.
17415317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    InputStream in = mSockets[i].getInputStream();
174297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    while (true) {
174397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        try {
174497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            if (in.read() == -1) {
174597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                                break;
174697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            }
174797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        } catch (Exception e) {
174897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            // ignore
174997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        }
175097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        checkpoint(true);
175197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    }
175285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
175385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
175497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Wait for the daemons to create the new state.
175597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                while (!state.exists()) {
17561f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Check if a running daemon is dead.
17571f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    for (int i = 0; i < mDaemons.length; ++i) {
17581f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        String daemon = mDaemons[i];
1759088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                        if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
17602e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                            throw new IllegalStateException(daemon + " is dead");
176185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
176285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
176385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    checkpoint(true);
176485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
176585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
176697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Now we are connected. Read and parse the new state.
1767c1bac3a6e240c1c9a14a7b515f585977fb908930Chia-chi Yeh                String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
17685026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                if (parameters.length != 7) {
176997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    throw new IllegalStateException("Cannot parse the state");
177097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
177197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
177297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the interface and the addresses in the config.
177397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                mConfig.interfaze = parameters[0].trim();
177485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
17754ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                mConfig.addLegacyAddresses(parameters[1]);
177697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the routes if they are not set in the config.
177797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (mConfig.routes == null || mConfig.routes.isEmpty()) {
17784ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                    mConfig.addLegacyRoutes(parameters[2]);
177997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
178097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
178197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the DNS servers if they are not set in the config.
178241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
178397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    String dnsServers = parameters[3].trim();
178441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    if (!dnsServers.isEmpty()) {
178541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                        mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
178641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    }
178741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                }
178841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
178997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the search domains if they are not set in the config.
179097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
179197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    String searchDomains = parameters[4].trim();
179297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    if (!searchDomains.isEmpty()) {
179397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
179497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    }
179597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
179697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
17975026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                // Add a throw route for the VPN server endpoint, if one was specified.
17985026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                String endpoint = parameters[5];
17995026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                if (!endpoint.isEmpty()) {
18005026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                    try {
18015026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        InetAddress addr = InetAddress.parseNumericAddress(endpoint);
18025026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        if (addr instanceof Inet4Address) {
18035026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                            mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 32), RTN_THROW));
18045026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        } else if (addr instanceof Inet6Address) {
18055026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                            mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 128), RTN_THROW));
18065026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        } else {
18075026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                            Log.e(TAG, "Unknown IP address family for VPN endpoint: " + endpoint);
18085026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        }
18095026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                    } catch (IllegalArgumentException e) {
18105026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        Log.e(TAG, "Exception constructing throw route to " + endpoint + ": " + e);
18115026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                    }
18125026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                }
18135026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti
181497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Here is the last step and it must be done synchronously.
181541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                synchronized (Vpn.this) {
18162b862e5e75ad419f17a4cea185b9349e0da70e7bVinit Deshapnde                    // Set the start time
18172b862e5e75ad419f17a4cea185b9349e0da70e7bVinit Deshapnde                    mConfig.startTime = SystemClock.elapsedRealtime();
18182b862e5e75ad419f17a4cea185b9349e0da70e7bVinit Deshapnde
181941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    // Check if the thread is interrupted while we are waiting.
182041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    checkpoint(false);
182141d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
1822e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                    // Check if the interface is gone while we are waiting.
1823c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                    if (jniCheck(mConfig.interfaze) == 0) {
182434e7813e962de99df9813014678ef5901227c5f1Chia-chi Yeh                        throw new IllegalStateException(mConfig.interfaze + " is gone");
182541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    }
1826e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh
1827e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                    // Now INetworkManagementEventObserver is watching our back.
1828c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                    mInterface = mConfig.interfaze;
18294d03abcd49af490dba3850d341b955dd72f24959Robin Lee                    prepareStatusIntent();
18306bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
18316bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    agentConnect();
18322e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
18332e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    Log.i(TAG, "Connected!");
183441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                }
183585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } catch (Exception e) {
18362e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                Log.i(TAG, "Aborting", e);
1837438406092ed71c658bf5a4e6ae2e7282fc4fab4dLorenzo Colitti                updateState(DetailedState.FAILED, e.getMessage());
1838e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                exit();
183985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
184085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
1841899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1842899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        /**
1843899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey         * Monitor the daemons we started, moving to disconnected state if the
1844899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey         * underlying services fail.
1845899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey         */
1846047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe        private void monitorDaemons() throws InterruptedException{
1847899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            if (!mNetworkInfo.isConnected()) {
1848899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                return;
1849899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
1850047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe            while (true) {
1851047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                Thread.sleep(2000);
1852047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                for (int i = 0; i < mDaemons.length; i++) {
1853047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
1854047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                        return;
1855899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    }
1856899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
1857899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
1858899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
185985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    }
1860ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh}
1861