Vpn.java revision e0be7e859b37438ec88780979e373821054a11ef
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;
842e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yehimport com.android.internal.net.LegacyVpnInfo;
8504ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yehimport com.android.internal.net.VpnConfig;
86f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tongimport com.android.internal.net.VpnInfo;
8782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport com.android.internal.net.VpnProfile;
88e0be7e859b37438ec88780979e373821054a11efChristopher Tateimport com.android.server.DeviceIdleController;
89e0be7e859b37438ec88780979e373821054a11efChristopher Tateimport com.android.server.LocalServices;
90899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkeyimport com.android.server.net.BaseNetworkObserver;
91ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
9205542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidsonimport libcore.io.IoUtils;
9305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
9497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yehimport java.io.File;
956bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidsonimport java.io.IOException;
9697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yehimport java.io.InputStream;
9785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yehimport java.io.OutputStream;
9882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkeyimport java.net.Inet4Address;
99f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandranimport java.net.Inet6Address;
100f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandranimport java.net.InetAddress;
101d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughesimport java.nio.charset.StandardCharsets;
1026bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport java.util.ArrayList;
10341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yehimport java.util.Arrays;
1044d03abcd49af490dba3850d341b955dd72f24959Robin Leeimport java.util.Collection;
1054d03abcd49af490dba3850d341b955dd72f24959Robin Leeimport java.util.Collections;
1066bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenimport java.util.List;
1074d03abcd49af490dba3850d341b955dd72f24959Robin Leeimport java.util.Set;
1080784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensenimport java.util.SortedSet;
1090784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensenimport java.util.TreeSet;
1101b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwaltimport java.util.concurrent.atomic.AtomicInteger;
11185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
112ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh/**
113ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh * @hide
114ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh */
1156bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensenpublic class Vpn {
1166bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private static final String NETWORKTYPE = "VPN";
117899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private static final String TAG = "Vpn";
118899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private static final boolean LOGD = true;
1196bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
120e0be7e859b37438ec88780979e373821054a11efChristopher Tate    // Length of time (in milliseconds) that an app hosting an always-on VPN is placed on
121e0be7e859b37438ec88780979e373821054a11efChristopher Tate    // the device idle whitelist during service launch and VPN bootstrap.
122e0be7e859b37438ec88780979e373821054a11efChristopher Tate    private static final long VPN_LAUNCH_IDLE_WHITELIST_DURATION = 60 * 1000;
123e0be7e859b37438ec88780979e373821054a11efChristopher Tate
124899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    // TODO: create separate trackers for each unique VPN to support
125899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    // automated reconnection
126199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
1276bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private Context mContext;
1286bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private NetworkInfo mNetworkInfo;
1296bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private String mPackage;
1306bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private int mOwnerUID;
131c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private String mInterface;
132199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    private Connection mConnection;
13385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    private LegacyVpnRunner mLegacyVpnRunner;
13490b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson    private PendingIntent mStatusIntent;
13557666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    private volatile boolean mEnableTeardown = true;
1366bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private final INetworkManagementService mNetd;
137c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private VpnConfig mConfig;
1386bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private NetworkAgent mNetworkAgent;
1396bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private final Looper mLooper;
1406bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private final NetworkCapabilities mNetworkCapabilities;
141c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
1424d03abcd49af490dba3850d341b955dd72f24959Robin Lee    /**
14317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
14417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * only applies to {@link VpnService} connections.
14517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     */
14617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    private boolean mAlwaysOn = false;
14717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
14817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    /**
14917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * Whether to disable traffic outside of this VPN even when the VPN is not connected. System
15017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * apps can still bypass by choosing explicit networks. Has no effect if {@link mAlwaysOn} is
15117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * not set.
15217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     */
15317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    private boolean mLockdown = false;
15417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
15517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    /**
1564d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * List of UIDs that are set to use this VPN by default. Normally, every UID in the user is
1574d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * added to this set but that can be changed by adding allowed or disallowed applications. It
1584d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * is non-null iff the VPN is connected.
1594d03abcd49af490dba3850d341b955dd72f24959Robin Lee     *
1604d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * Unless the VPN has set allowBypass=true, these UIDs are forced into the VPN.
1614d03abcd49af490dba3850d341b955dd72f24959Robin Lee     *
1624d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @see VpnService.Builder#addAllowedApplication(String)
1634d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @see VpnService.Builder#addDisallowedApplication(String)
1644d03abcd49af490dba3850d341b955dd72f24959Robin Lee     */
165c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    @GuardedBy("this")
1664d03abcd49af490dba3850d341b955dd72f24959Robin Lee    private Set<UidRange> mVpnUsers = null;
167c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
16817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    /**
16917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * List of UIDs for which networking should be blocked until VPN is ready, during brief periods
17017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * when VPN is not running. For example, during system startup or after a crash.
17117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @see mLockdown
17217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     */
17317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    @GuardedBy("this")
17417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    private Set<UidRange> mBlockedUsers = new ArraySet<>();
17517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
1760784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    // Handle of user initiating VPN.
1770784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private final int mUserHandle;
178ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
179812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    // Listen to package remove and change event in this user
180812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
181812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        @Override
182812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        public void onReceive(Context context, Intent intent) {
183812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            final Uri data = intent.getData();
184812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            final String packageName = data == null ? null : data.getSchemeSpecificPart();
185812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            if (packageName == null) {
186812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                return;
187812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            }
188812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
189812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            synchronized (Vpn.this) {
190812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                // Avoid race that always-on package has been unset
191812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                if (!packageName.equals(getAlwaysOnPackage())) {
192812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                    return;
193812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                }
194812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
195812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                final String action = intent.getAction();
196812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                Log.i(TAG, "Received broadcast " + action + " for always-on package " + packageName
197812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                        + " in user " + mUserHandle);
198812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
199812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                switch(action) {
200812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                    case Intent.ACTION_PACKAGE_REPLACED:
201812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                        // Start vpn after app upgrade
202812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                        startAlwaysOnVpn();
203812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                        break;
204812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                    case Intent.ACTION_PACKAGE_REMOVED:
205812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                        final boolean isPackageRemoved = !intent.getBooleanExtra(
206812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                                Intent.EXTRA_REPLACING, false);
207812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                        if (isPackageRemoved) {
208812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                            setAndSaveAlwaysOnPackage(null, false);
209812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                        }
210812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                        break;
211812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                }
212812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            }
213812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        }
214812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    };
215812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
216812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    private boolean mIsPackageIntentReceiverRegistered = false;
217812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
2186bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    public Vpn(Looper looper, Context context, INetworkManagementService netService,
219e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen            int userHandle) {
220ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        mContext = context;
2216bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetd = netService;
2220784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        mUserHandle = userHandle;
2236bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mLooper = looper;
2246bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
2256bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mPackage = VpnConfig.LEGACY_VPN;
2260784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        mOwnerUID = getAppUid(mPackage, mUserHandle);
227899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
228899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        try {
229899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            netService.registerObserver(mObserver);
230899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        } catch (RemoteException e) {
231899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            Log.wtf(TAG, "Problem registering observer", e);
232899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
2336bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
2346bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
2356bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        // TODO: Copy metered attribute and bandwidths from physical transport, b/16207332
2366bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkCapabilities = new NetworkCapabilities();
2376bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
2386bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
239899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    }
240899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
24157666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    /**
24257666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * Set if this object is responsible for watching for {@link NetworkInfo}
24357666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * teardown. When {@code false}, teardown is handled externally by someone
24457666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     * else.
24557666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey     */
24657666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    public void setEnableTeardown(boolean enableTeardown) {
24757666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey        mEnableTeardown = enableTeardown;
24857666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey    }
24957666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey
250899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    /**
251899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey     * Update current state, dispaching event to listeners.
252899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey     */
2531a405fe300950d6ceae2166fd074b596d8110dbeTony Mak    @VisibleForTesting
2541a405fe300950d6ceae2166fd074b596d8110dbeTony Mak    protected void updateState(DetailedState detailedState, String reason) {
255899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
256899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        mNetworkInfo.setDetailedState(detailedState, reason, null);
2576bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mNetworkAgent != null) {
2586bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
2596bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
2601a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        updateAlwaysOnNotification(detailedState);
261ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
262ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
263ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    /**
264244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     * Configures an always-on VPN connection through a specific application.
265244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     * This connection is automatically granted and persisted after a reboot.
266244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     *
267244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     * <p>The designated package should exist and declare a {@link VpnService} in its
268244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     *    manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
269244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     *    otherwise the call will fail.
270244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     *
27117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @param packageName the package to designate as always-on VPN supplier.
27217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
2739ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee     * @return {@code true} if the package has been set as always-on, {@code false} otherwise.
274244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     */
27517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    public synchronized boolean setAlwaysOnPackage(String packageName, boolean lockdown) {
276244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        enforceControlPermissionOrInternalCaller();
2779ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee        if (VpnConfig.LEGACY_VPN.equals(packageName)) {
2789ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee            Log.w(TAG, "Not setting legacy VPN \"" + packageName + "\" as always-on.");
2799ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee            return false;
2809ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee        }
281244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee
282244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        if (packageName != null) {
2839ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee            // Pre-authorize new always-on VPN package.
284244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee            if (!setPackageAuthorization(packageName, true)) {
285244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee                return false;
286244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee            }
2879ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee            mAlwaysOn = true;
2889ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee        } else {
2899ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee            packageName = VpnConfig.LEGACY_VPN;
2909ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee            mAlwaysOn = false;
291244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        }
292244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee
29317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        mLockdown = (mAlwaysOn && lockdown);
2941a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        if (isCurrentPreparedPackage(packageName)) {
2951a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            updateAlwaysOnNotification(mNetworkInfo.getDetailedState());
2961a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        } else {
2971a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            // Prepare this app. The notification will update as a side-effect of updateState().
2989ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee            prepareInternal(packageName);
2999ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee        }
300812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        maybeRegisterPackageChangeReceiverLocked(packageName);
30117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        setVpnForcedLocked(mLockdown);
302244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        return true;
303244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee    }
304244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee
3059ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee    private static boolean isNullOrLegacyVpn(String packageName) {
3069ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee        return packageName == null || VpnConfig.LEGACY_VPN.equals(packageName);
3079ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee    }
3089ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee
309812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    private void unregisterPackageChangeReceiverLocked() {
310812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        // register previous intent filter
311812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        if (mIsPackageIntentReceiverRegistered) {
312812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            mContext.unregisterReceiver(mPackageIntentReceiver);
313812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            mIsPackageIntentReceiverRegistered = false;
314812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        }
315812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    }
316812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
317812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    private void maybeRegisterPackageChangeReceiverLocked(String packageName) {
318812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        // Unregister IntentFilter listening for previous always-on package change
319812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        unregisterPackageChangeReceiverLocked();
320812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
3219ff1a588786cb2963d76c75f3a077fc17fa1589cRobin Lee        if (!isNullOrLegacyVpn(packageName)) {
322812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            mIsPackageIntentReceiverRegistered = true;
323812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
324812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            IntentFilter intentFilter = new IntentFilter();
325812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            // Protected intent can only be sent by system. No permission required in register.
326812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
327812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
328812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            intentFilter.addDataScheme("package");
329812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            intentFilter.addDataSchemeSpecificPart(packageName, PatternMatcher.PATTERN_LITERAL);
330812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            mContext.registerReceiverAsUser(
331812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                    mPackageIntentReceiver, UserHandle.of(mUserHandle), intentFilter, null, null);
332812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        }
333812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    }
334812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
335244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee    /**
336244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     * @return the package name of the VPN controller responsible for always-on VPN,
337244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     *         or {@code null} if none is set or always-on VPN is controlled through
338244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     *         lockdown instead.
339244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     * @hide
340244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee     */
341244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee    public synchronized String getAlwaysOnPackage() {
342244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        enforceControlPermissionOrInternalCaller();
34317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        return (mAlwaysOn ? mPackage : null);
344244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee    }
345244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee
346244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee    /**
347812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * Save the always-on package and lockdown config into Settings.Secure
348812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     */
349812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    public synchronized void saveAlwaysOnPackage() {
350812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        final long token = Binder.clearCallingIdentity();
351812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        try {
352812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            final ContentResolver cr = mContext.getContentResolver();
353812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            Settings.Secure.putStringForUser(cr, Settings.Secure.ALWAYS_ON_VPN_APP,
354812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                    getAlwaysOnPackage(), mUserHandle);
355812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            Settings.Secure.putIntForUser(cr, Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
356812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                    (mLockdown ? 1 : 0), mUserHandle);
357812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        } finally {
358812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            Binder.restoreCallingIdentity(token);
359812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        }
360812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    }
361812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
362812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    /**
363812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * Set and save always-on package and lockdown config
364812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * @see Vpn#setAlwaysOnPackage(String, boolean)
365812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * @see Vpn#saveAlwaysOnPackage()
366812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     *
367812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * @return result of Vpn#setAndSaveAlwaysOnPackage(String, boolean)
368812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     */
369812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    private synchronized boolean setAndSaveAlwaysOnPackage(String packageName, boolean lockdown) {
370812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        if (setAlwaysOnPackage(packageName, lockdown)) {
371812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            saveAlwaysOnPackage();
372812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            return true;
373812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        } else {
374812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            return false;
375812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        }
376812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    }
377812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
378812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    /**
379812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * @return {@code true} if the service was started, the service was already connected, or there
380812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     *         was no always-on VPN to start. {@code false} otherwise.
381812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     */
382812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    public boolean startAlwaysOnVpn() {
383812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        final String alwaysOnPackage;
384812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        synchronized (this) {
385812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            alwaysOnPackage = getAlwaysOnPackage();
386812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            // Skip if there is no service to start.
387812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            if (alwaysOnPackage == null) {
388812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                return true;
389812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            }
390812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            // Skip if the service is already established. This isn't bulletproof: it's not bound
391812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            // until after establish(), so if it's mid-setup onStartCommand will be sent twice,
392812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            // which may restart the connection.
393812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            if (getNetworkInfo().isConnected()) {
394812800cb92090db31f609b907c4458ba76cf7f42Robin Lee                return true;
395812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            }
396812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        }
397812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
398e0be7e859b37438ec88780979e373821054a11efChristopher Tate        // Tell the OS that background services in this app need to be allowed for
399e0be7e859b37438ec88780979e373821054a11efChristopher Tate        // a short time, so we can bootstrap the VPN service.
400e0be7e859b37438ec88780979e373821054a11efChristopher Tate        final long oldId = Binder.clearCallingIdentity();
401812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        try {
402e0be7e859b37438ec88780979e373821054a11efChristopher Tate            DeviceIdleController.LocalService idleController =
403e0be7e859b37438ec88780979e373821054a11efChristopher Tate                    LocalServices.getService(DeviceIdleController.LocalService.class);
404e0be7e859b37438ec88780979e373821054a11efChristopher Tate            idleController.addPowerSaveTempWhitelistApp(Process.myUid(), alwaysOnPackage,
405e0be7e859b37438ec88780979e373821054a11efChristopher Tate                    VPN_LAUNCH_IDLE_WHITELIST_DURATION, mUserHandle, false, "vpn");
406e0be7e859b37438ec88780979e373821054a11efChristopher Tate
407e0be7e859b37438ec88780979e373821054a11efChristopher Tate            // Start the VPN service declared in the app's manifest.
408e0be7e859b37438ec88780979e373821054a11efChristopher Tate            Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
409e0be7e859b37438ec88780979e373821054a11efChristopher Tate            serviceIntent.setPackage(alwaysOnPackage);
410e0be7e859b37438ec88780979e373821054a11efChristopher Tate            try {
411e0be7e859b37438ec88780979e373821054a11efChristopher Tate                return mContext.startServiceAsUser(serviceIntent, UserHandle.of(mUserHandle)) != null;
412e0be7e859b37438ec88780979e373821054a11efChristopher Tate            } catch (RuntimeException e) {
413e0be7e859b37438ec88780979e373821054a11efChristopher Tate                Log.e(TAG, "VpnService " + serviceIntent + " failed to start", e);
414e0be7e859b37438ec88780979e373821054a11efChristopher Tate                return false;
415e0be7e859b37438ec88780979e373821054a11efChristopher Tate            }
416e0be7e859b37438ec88780979e373821054a11efChristopher Tate        } finally {
417e0be7e859b37438ec88780979e373821054a11efChristopher Tate            Binder.restoreCallingIdentity(oldId);
418812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        }
419812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    }
420812800cb92090db31f609b907c4458ba76cf7f42Robin Lee
421812800cb92090db31f609b907c4458ba76cf7f42Robin Lee    /**
422100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * Prepare for a VPN application. This method is designed to solve
423100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * race conditions. It first compares the current prepared package
424100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * with {@code oldPackage}. If they are the same, the prepared
425100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * package is revoked and replaced with {@code newPackage}. If
426100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * {@code oldPackage} is {@code null}, the comparison is omitted.
427100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * If {@code newPackage} is the same package or {@code null}, the
428100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * revocation is omitted. This method returns {@code true} if the
429100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * operation is succeeded.
430e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     *
431100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * Legacy VPN is handled specially since it is not a real package.
432100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
433100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * it can be revoked by itself.
434100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     *
43598a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     * Note: when we added VPN pre-consent in http://ag/522961 the names oldPackage
43698a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     * and newPackage become misleading, because when an app is pre-consented, we
43798a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     * actually prepare oldPackage, not newPackage.
43898a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     *
43998a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     * Their meanings actually are:
44098a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     *
44198a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     * - oldPackage non-null, newPackage null: App calling VpnService#prepare().
44298a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     * - oldPackage null, newPackage non-null: ConfirmDialog calling prepareVpn().
443812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * - oldPackage null, newPackage=LEGACY_VPN: Used internally to disconnect
44498a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     *   and revoke any current app VPN and re-prepare legacy vpn.
44598a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     *
446812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * TODO: Rename the variables - or split this method into two - and end this confusion.
447812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * TODO: b/29032008 Migrate code from prepare(oldPackage=non-null, newPackage=LEGACY_VPN)
448812800cb92090db31f609b907c4458ba76cf7f42Robin Lee     * to prepare(oldPackage=null, newPackage=LEGACY_VPN)
44998a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     *
45098a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     * @param oldPackage The package name of the old VPN application
45198a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     * @param newPackage The package name of the new VPN application
45298a633a89cf3223f79ea625323ec2b91bee72584Victor Chang     *
453100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * @return true if the operation is succeeded.
454ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
455100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh    public synchronized boolean prepare(String oldPackage, String newPackage) {
4560a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson        if (oldPackage != null) {
45798a633a89cf3223f79ea625323ec2b91bee72584Victor Chang            // Stop an existing always-on VPN from being dethroned by other apps.
458812800cb92090db31f609b907c4458ba76cf7f42Robin Lee            if (mAlwaysOn && !isCurrentPreparedPackage(oldPackage)) {
45998a633a89cf3223f79ea625323ec2b91bee72584Victor Chang                return false;
46098a633a89cf3223f79ea625323ec2b91bee72584Victor Chang            }
46198a633a89cf3223f79ea625323ec2b91bee72584Victor Chang
46298a633a89cf3223f79ea625323ec2b91bee72584Victor Chang            // Package is not same or old package was reinstalled.
46398a633a89cf3223f79ea625323ec2b91bee72584Victor Chang            if (!isCurrentPreparedPackage(oldPackage)) {
4640a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                // The package doesn't match. We return false (to obtain user consent) unless the
4650a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                // user has already consented to that VPN package.
4660a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) {
4670a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                    prepareInternal(oldPackage);
4680a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                    return true;
4690a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                }
4700a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                return false;
4710a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson            } else if (!oldPackage.equals(VpnConfig.LEGACY_VPN)
4720a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                    && !isVpnUserPreConsented(oldPackage)) {
4730a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                // Currently prepared VPN is revoked, so unprepare it and return false.
4740a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                prepareInternal(VpnConfig.LEGACY_VPN);
4750a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson                return false;
47605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            }
477100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        }
478100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh
479100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        // Return true if we do not need to revoke.
480be08587510edbc149c841638db721eb97d2351b6Jeff Davidson        if (newPackage == null || (!newPackage.equals(VpnConfig.LEGACY_VPN) &&
48198a633a89cf3223f79ea625323ec2b91bee72584Victor Chang                isCurrentPreparedPackage(newPackage))) {
482100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh            return true;
483ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
484ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
4851b1bcd7b7370866bc00ce3361be8d5167bf3e28dRobin Lee        // Check that the caller is authorized.
486dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        enforceControlPermission();
4877b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh
48898a633a89cf3223f79ea625323ec2b91bee72584Victor Chang        // Stop an existing always-on VPN from being dethroned by other apps.
489812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        if (mAlwaysOn && !isCurrentPreparedPackage(newPackage)) {
49098a633a89cf3223f79ea625323ec2b91bee72584Victor Chang            return false;
49198a633a89cf3223f79ea625323ec2b91bee72584Victor Chang        }
49298a633a89cf3223f79ea625323ec2b91bee72584Victor Chang
49311008a78b8e30910cedd8b8431980c7738183292Jeff Davidson        prepareInternal(newPackage);
49411008a78b8e30910cedd8b8431980c7738183292Jeff Davidson        return true;
49511008a78b8e30910cedd8b8431980c7738183292Jeff Davidson    }
49611008a78b8e30910cedd8b8431980c7738183292Jeff Davidson
49798a633a89cf3223f79ea625323ec2b91bee72584Victor Chang    private boolean isCurrentPreparedPackage(String packageName) {
49898a633a89cf3223f79ea625323ec2b91bee72584Victor Chang        // We can't just check that packageName matches mPackage, because if the app was uninstalled
49998a633a89cf3223f79ea625323ec2b91bee72584Victor Chang        // and reinstalled it will no longer be prepared. Instead check the UID.
50098a633a89cf3223f79ea625323ec2b91bee72584Victor Chang        return getAppUid(packageName, mUserHandle) == mOwnerUID;
50198a633a89cf3223f79ea625323ec2b91bee72584Victor Chang    }
50298a633a89cf3223f79ea625323ec2b91bee72584Victor Chang
50311008a78b8e30910cedd8b8431980c7738183292Jeff Davidson    /** Prepare the VPN for the given package. Does not perform permission checks. */
50411008a78b8e30910cedd8b8431980c7738183292Jeff Davidson    private void prepareInternal(String newPackage) {
50511008a78b8e30910cedd8b8431980c7738183292Jeff Davidson        long token = Binder.clearCallingIdentity();
50611008a78b8e30910cedd8b8431980c7738183292Jeff Davidson        try {
50711008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            // Reset the interface.
50811008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            if (mInterface != null) {
50911008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mStatusIntent = null;
51011008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                agentDisconnect();
51111008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                jniReset(mInterface);
51211008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mInterface = null;
51311008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mVpnUsers = null;
51411008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            }
51511008a78b8e30910cedd8b8431980c7738183292Jeff Davidson
51611008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            // Revoke the connection or stop LegacyVpnRunner.
51711008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            if (mConnection != null) {
51811008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                try {
51911008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                    mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
52011008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                            Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
52111008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                } catch (Exception e) {
52211008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                    // ignore
52311008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                }
52411008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mContext.unbindService(mConnection);
52511008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mConnection = null;
52611008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            } else if (mLegacyVpnRunner != null) {
52711008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mLegacyVpnRunner.exit();
52811008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mLegacyVpnRunner = null;
52911008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            }
530ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
531199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            try {
53211008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mNetd.denyProtect(mOwnerUID);
533199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            } catch (Exception e) {
53411008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
535199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
53641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
53711008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
53811008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            mPackage = newPackage;
53911008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            mOwnerUID = getAppUid(newPackage, mUserHandle);
54011008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            try {
54111008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                mNetd.allowProtect(mOwnerUID);
54211008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            } catch (Exception e) {
54311008a78b8e30910cedd8b8431980c7738183292Jeff Davidson                Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
54411008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            }
54511008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            mConfig = null;
5466bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
54711008a78b8e30910cedd8b8431980c7738183292Jeff Davidson            updateState(DetailedState.IDLE, "prepare");
5486bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } finally {
5496bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            Binder.restoreCallingIdentity(token);
5506bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
551ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
552ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
55305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    /**
5543b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee     * Set whether a package has the ability to launch VPNs without user intervention.
55505542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson     */
556244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee    public boolean setPackageAuthorization(String packageName, boolean authorized) {
55705542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        // Check if the caller is authorized.
558244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        enforceControlPermissionOrInternalCaller();
55905542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
5603b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee        int uid = getAppUid(packageName, mUserHandle);
5613b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee        if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
5623b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee            // Authorization for nonexistent packages (or fake ones) can't be updated.
563244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee            return false;
56405542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        }
56505542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
56605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        long token = Binder.clearCallingIdentity();
56705542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        try {
56805542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            AppOpsManager appOps =
56905542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                    (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
5703b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee            appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, uid, packageName,
57105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                    authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
572244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee            return true;
57305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        } catch (Exception e) {
5743b3dd942ec6a0beaccd1cef0723d72786435d8f3Robin Lee            Log.wtf(TAG, "Failed to set app ops for package " + packageName + ", uid " + uid, e);
57505542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        } finally {
57605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson            Binder.restoreCallingIdentity(token);
57705542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        }
578244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        return false;
57905542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    }
58005542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
58105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    private boolean isVpnUserPreConsented(String packageName) {
58205542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        AppOpsManager appOps =
58305542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
58405542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
58505542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        // Verify that the caller matches the given package and has permission to activate VPNs.
58605542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        return appOps.noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Binder.getCallingUid(),
58705542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                packageName) == AppOpsManager.MODE_ALLOWED;
58805542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson    }
58905542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson
5900784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private int getAppUid(String app, int userHandle) {
59105542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson        if (VpnConfig.LEGACY_VPN.equals(app)) {
5926bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            return Process.myUid();
5936bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
594fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        PackageManager pm = mContext.getPackageManager();
5956bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        int result;
5966bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        try {
597e06b4d1d9f718b9fe02980fea794a36831a16db2Jeff Sharkey            result = pm.getPackageUidAsUser(app, userHandle);
5986bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        } catch (NameNotFoundException e) {
5996bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            result = -1;
600fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        }
6016bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        return result;
6026bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
6036bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
6046bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    public NetworkInfo getNetworkInfo() {
6056bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        return mNetworkInfo;
6066bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
6076bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
60831a94f48bf8014cf6a1127bd23cf9a8541a9abedPaul Jensen    public int getNetId() {
60931a94f48bf8014cf6a1127bd23cf9a8541a9abedPaul Jensen        return mNetworkAgent != null ? mNetworkAgent.netId : NETID_UNSET;
61031a94f48bf8014cf6a1127bd23cf9a8541a9abedPaul Jensen    }
61131a94f48bf8014cf6a1127bd23cf9a8541a9abedPaul Jensen
61260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti    private LinkProperties makeLinkProperties() {
61360446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        boolean allowIPv4 = mConfig.allowIPv4;
61460446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        boolean allowIPv6 = mConfig.allowIPv6;
61560446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
6166bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        LinkProperties lp = new LinkProperties();
61760446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
6186bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        lp.setInterfaceName(mInterface);
61942065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
62060446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        if (mConfig.addresses != null) {
62160446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            for (LinkAddress address : mConfig.addresses) {
62260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                lp.addLinkAddress(address);
62360446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv4 |= address.getAddress() instanceof Inet4Address;
62460446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv6 |= address.getAddress() instanceof Inet6Address;
62560446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            }
6266bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
62760446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
62860446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        if (mConfig.routes != null) {
62960446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            for (RouteInfo route : mConfig.routes) {
63060446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                lp.addRoute(route);
63160446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                InetAddress address = route.getDestination().getAddress();
63260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv4 |= address instanceof Inet4Address;
63360446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv6 |= address instanceof Inet6Address;
63460446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            }
6356bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
63642065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
6376bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mConfig.dnsServers != null) {
6386bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            for (String dnsServer : mConfig.dnsServers) {
63942065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran                InetAddress address = InetAddress.parseNumericAddress(dnsServer);
64042065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran                lp.addDnsServer(address);
64160446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv4 |= address instanceof Inet4Address;
64260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti                allowIPv6 |= address instanceof Inet6Address;
6436bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
6446bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
64542065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
64660446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        if (!allowIPv4) {
64760446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
64860446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        }
64960446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        if (!allowIPv6) {
65060446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
65160446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        }
65260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
6536bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        // Concatenate search domains into a string.
6546bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        StringBuilder buffer = new StringBuilder();
6556bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mConfig.searchDomains != null) {
6566bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            for (String domain : mConfig.searchDomains) {
6576bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                buffer.append(domain).append(' ');
6586bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            }
6596bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
6606bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        lp.setDomains(buffer.toString().trim());
66142065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
66260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        // TODO: Stop setting the MTU in jniCreate and set it here.
66360446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
66460446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        return lp;
66560446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti    }
66660446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
66760446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti    private void agentConnect() {
66860446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        LinkProperties lp = makeLinkProperties();
66960446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
67060446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        if (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute()) {
67160446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
67260446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        } else {
67360446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti            mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
67460446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti        }
67560446165d8bd44f72cec8d0c5583a688369fa660Lorenzo Colitti
676323f29df583e9338e3b2bf90fc8c0785a934a61bRobin Lee        mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null);
67742065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
6788cd33ed84e94036a5e1201485af7603dc6fb0d9bSreeram Ramachandran        NetworkMisc networkMisc = new NetworkMisc();
67917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        networkMisc.allowBypass = mConfig.allowBypass && !mLockdown;
68042065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
6816bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        long token = Binder.clearCallingIdentity();
6824ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        try {
6836bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
6848cd33ed84e94036a5e1201485af7603dc6fb0d9bSreeram Ramachandran                    mNetworkInfo, mNetworkCapabilities, lp, 0, networkMisc) {
68505542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                            @Override
6866bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                            public void unwanted() {
6876bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                                // We are user controlled, not driven by NetworkRequest.
68805542603dd4f1e0ea47a3dca01de3999a9a329a9Jeff Davidson                            }
6896bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        };
6904ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } finally {
6914ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            Binder.restoreCallingIdentity(token);
6924ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        }
69342065ac64cba166dc0fe602957ea8fe80bf406e2Sreeram Ramachandran
6944d03abcd49af490dba3850d341b955dd72f24959Robin Lee        mVpnUsers = createUserAndRestrictedProfilesRanges(mUserHandle,
6954d03abcd49af490dba3850d341b955dd72f24959Robin Lee                mConfig.allowedApplications, mConfig.disallowedApplications);
6966bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
697323f29df583e9338e3b2bf90fc8c0785a934a61bRobin Lee
698323f29df583e9338e3b2bf90fc8c0785a934a61bRobin Lee        mNetworkInfo.setIsAvailable(true);
699323f29df583e9338e3b2bf90fc8c0785a934a61bRobin Lee        updateState(DetailedState.CONNECTED, "agentConnect");
7006bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
7016bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
7021c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov    private boolean canHaveRestrictedProfile(int userId) {
7031c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov        long token = Binder.clearCallingIdentity();
7041c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov        try {
7051c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov            return UserManager.get(mContext).canHaveRestrictedProfile(userId);
7061c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov        } finally {
7071c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov            Binder.restoreCallingIdentity(token);
7081c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov        }
7091c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov    }
7101c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov
7111a405fe300950d6ceae2166fd074b596d8110dbeTony Mak    private void agentDisconnect(NetworkAgent networkAgent) {
7126bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (networkAgent != null) {
7131a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
7141a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            networkInfo.setIsAvailable(false);
7151a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
7166bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            networkAgent.sendNetworkInfo(networkInfo);
7176bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
7186bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    }
7196bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
7206bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen    private void agentDisconnect() {
7216bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (mNetworkInfo.isConnected()) {
7221a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            mNetworkInfo.setIsAvailable(false);
7231a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            updateState(DetailedState.DISCONNECTED, "agentDisconnect");
7246bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent = null;
7256bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        }
726fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    }
727fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh
728fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh    /**
729e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * Establish a VPN network and return the file descriptor of the VPN
730e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * interface. This methods returns {@code null} if the application is
731100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh     * revoked or not prepared.
732ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     *
733e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @param config The parameters to configure the network.
734e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh     * @return The file descriptor of the VPN interface.
735ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh     */
73604ba25c418bc4538e9dc0f047cfb9608d358f679Chia-chi Yeh    public synchronized ParcelFileDescriptor establish(VpnConfig config) {
737ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        // Check if the caller is already prepared.
738c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        UserManager mgr = UserManager.get(mContext);
7396bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        if (Binder.getCallingUid() != mOwnerUID) {
7407b0b834c3d12564c44ac134879a6dbc70e74be6eChia-chi Yeh            return null;
741ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
7420a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson        // Check to ensure consent hasn't been revoked since we were prepared.
7430a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson        if (!isVpnUserPreConsented(mPackage)) {
7440a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson            return null;
7450a775ce9801f03071d1e9bcc177d79e6fe350702Jeff Davidson        }
746fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh        // Check if the service is properly declared.
747199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
748199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        intent.setClassName(mPackage, config.user);
7494ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        long token = Binder.clearCallingIdentity();
7504ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        try {
751c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            // Restricted users are not allowed to create VPNs, they are tied to Owner
7520784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            UserInfo user = mgr.getUserInfo(mUserHandle);
753628ae0d84180c5f7c52725e02506021e532ed252Robin Lee            if (user.isRestricted()) {
754c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                throw new SecurityException("Restricted users cannot establish VPNs");
755c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
756c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
7574ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
7580784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                                                                        null, 0, mUserHandle);
7594ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (info == null) {
7604ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new SecurityException("Cannot find " + config.user);
7614ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            }
7624ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
7634ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
7644ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            }
7654ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } catch (RemoteException e) {
7664ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new SecurityException("Cannot find " + config.user);
7674ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        } finally {
7684ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            Binder.restoreCallingIdentity(token);
769199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
770fcc1b41b663c1a0cb551344c4a16a5ad9ce36d60Chia-chi Yeh
7714c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        // Save the old config in case we need to go back.
7724c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        VpnConfig oldConfig = mConfig;
7734c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        String oldInterface = mInterface;
7744c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker        Connection oldConnection = mConnection;
7756bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        NetworkAgent oldNetworkAgent = mNetworkAgent;
7766bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen        mNetworkAgent = null;
7774d03abcd49af490dba3850d341b955dd72f24959Robin Lee        Set<UidRange> oldUsers = mVpnUsers;
7784c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker
779e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh        // Configure the interface. Abort if any of these steps fails.
78097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh        ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
781ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        try {
782899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            updateState(DetailedState.CONNECTING, "establish");
783c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            String interfaze = jniGetName(tun.getFd());
7844ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
785c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            // TEMP use the old jni calls until there is support for netd address setting
7864ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            StringBuilder builder = new StringBuilder();
7874ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            for (LinkAddress address : config.addresses) {
7884ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                builder.append(" " + address);
78997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            }
7904ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            if (jniSetAddresses(interfaze, builder.toString()) < 1) {
7914ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                throw new IllegalArgumentException("At least one address must be specified");
79297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            }
793199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            Connection connection = new Connection();
794d69e4c1460017062e7c36be55801cb434ad19d97Dianne Hackborn            if (!mContext.bindServiceAsUser(intent, connection,
795d69e4c1460017062e7c36be55801cb434ad19d97Dianne Hackborn                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
796d69e4c1460017062e7c36be55801cb434ad19d97Dianne Hackborn                    new UserHandle(mUserHandle))) {
797199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh                throw new IllegalStateException("Cannot bind " + config.user);
798199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
7994c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker
800199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mConnection = connection;
801c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh            mInterface = interfaze;
8024ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
8034ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            // Fill more values.
8044ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            config.user = mPackage;
8054ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            config.interfaze = mInterface;
806c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            config.startTime = SystemClock.elapsedRealtime();
807c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            mConfig = config;
8084c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker
8094ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            // Set up forwarding and DNS rules.
8106bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentConnect();
8114ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
8124c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            if (oldConnection != null) {
8134c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker                mContext.unbindService(oldConnection);
8144c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            }
8156bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            // Remove the old tun's user forwarding rules
8166bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            // The new tun's user rules have already been added so they will take over
8176bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            // as rules are deleted. This prevents data leakage as the rules are moved over.
8186bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect(oldNetworkAgent);
8194c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            if (oldInterface != null && !oldInterface.equals(interfaze)) {
8204c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker                jniReset(oldInterface);
8214c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            }
8226bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson
8236bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson            try {
8246bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson                IoUtils.setBlocking(tun.getFileDescriptor(), config.blocking);
8256bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson            } catch (IOException e) {
8266bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson                throw new IllegalStateException(
8276bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson                        "Cannot set tunnel's fd as blocking=" + config.blocking, e);
8286bbf39cf6b81222f32d2b66b8fa85d562e0ad71cJeff Davidson            }
829ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        } catch (RuntimeException e) {
830065b299df4159602327977dd007cb2cd6b64ab20Jeff Sharkey            IoUtils.closeQuietly(tun);
8316bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect();
8324c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            // restore old state
8334c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mConfig = oldConfig;
8344c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mConnection = oldConnection;
8354c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mVpnUsers = oldUsers;
8366bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            mNetworkAgent = oldNetworkAgent;
8374c5c33e5e6fb37f85977c70a0baba4e1ed51fe0dChad Brubaker            mInterface = oldInterface;
838ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh            throw e;
839ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
840199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        Log.i(TAG, "Established by " + config.user + " on " + mInterface);
841c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh        return tun;
842ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
843ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
844c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    private boolean isRunningLocked() {
845c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        return mNetworkAgent != null && mInterface != null;
846c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    }
847c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran
848c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    // Returns true if the VPN has been established and the calling UID is its owner. Used to check
849c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    // that a call to mutate VPN state is admissible.
850c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    private boolean isCallerEstablishedOwnerLocked() {
851c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        return isRunningLocked() && Binder.getCallingUid() == mOwnerUID;
852c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
853c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
8540784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    // Note: Return type guarantees results are deduped and sorted, which callers require.
8550784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private SortedSet<Integer> getAppsUids(List<String> packageNames, int userHandle) {
8560784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        SortedSet<Integer> uids = new TreeSet<Integer>();
8570784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        for (String app : packageNames) {
8580784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            int uid = getAppUid(app, userHandle);
8590784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            if (uid != -1) uids.add(uid);
8600784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        }
8610784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        return uids;
8620784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    }
8630784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen
8644d03abcd49af490dba3850d341b955dd72f24959Robin Lee    /**
8654d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * Creates a {@link Set} of non-intersecting {@link UidRange} objects including all UIDs
8664d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * associated with one user, and any restricted profiles attached to that user.
8674d03abcd49af490dba3850d341b955dd72f24959Robin Lee     *
8684d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * <p>If one of {@param allowedApplications} or {@param disallowedApplications} is provided,
8694d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * the UID ranges will match the app whitelist or blacklist specified there. Otherwise, all UIDs
8704d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * in each user and profile will be included.
8714d03abcd49af490dba3850d341b955dd72f24959Robin Lee     *
8724d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @param userHandle The userId to create UID ranges for along with any of its restricted
8734d03abcd49af490dba3850d341b955dd72f24959Robin Lee     *                   profiles.
8744d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @param allowedApplications (optional) whitelist of applications to include.
8754d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @param disallowedApplications (optional) blacklist of applications to exclude.
8764d03abcd49af490dba3850d341b955dd72f24959Robin Lee     */
8774d03abcd49af490dba3850d341b955dd72f24959Robin Lee    @VisibleForTesting
8784d03abcd49af490dba3850d341b955dd72f24959Robin Lee    Set<UidRange> createUserAndRestrictedProfilesRanges(@UserIdInt int userHandle,
8794d03abcd49af490dba3850d341b955dd72f24959Robin Lee            @Nullable List<String> allowedApplications,
8804d03abcd49af490dba3850d341b955dd72f24959Robin Lee            @Nullable List<String> disallowedApplications) {
8814d03abcd49af490dba3850d341b955dd72f24959Robin Lee        final Set<UidRange> ranges = new ArraySet<>();
8824d03abcd49af490dba3850d341b955dd72f24959Robin Lee
8834d03abcd49af490dba3850d341b955dd72f24959Robin Lee        // Assign the top-level user to the set of ranges
8844d03abcd49af490dba3850d341b955dd72f24959Robin Lee        addUserToRanges(ranges, userHandle, allowedApplications, disallowedApplications);
8854d03abcd49af490dba3850d341b955dd72f24959Robin Lee
8864d03abcd49af490dba3850d341b955dd72f24959Robin Lee        // If the user can have restricted profiles, assign all its restricted profiles too
8874d03abcd49af490dba3850d341b955dd72f24959Robin Lee        if (canHaveRestrictedProfile(userHandle)) {
8884d03abcd49af490dba3850d341b955dd72f24959Robin Lee            final long token = Binder.clearCallingIdentity();
8894d03abcd49af490dba3850d341b955dd72f24959Robin Lee            List<UserInfo> users;
8904d03abcd49af490dba3850d341b955dd72f24959Robin Lee            try {
89117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                users = UserManager.get(mContext).getUsers(true);
8924d03abcd49af490dba3850d341b955dd72f24959Robin Lee            } finally {
8934d03abcd49af490dba3850d341b955dd72f24959Robin Lee                Binder.restoreCallingIdentity(token);
8944d03abcd49af490dba3850d341b955dd72f24959Robin Lee            }
8954d03abcd49af490dba3850d341b955dd72f24959Robin Lee            for (UserInfo user : users) {
8964d03abcd49af490dba3850d341b955dd72f24959Robin Lee                if (user.isRestricted() && (user.restrictedProfileParentId == userHandle)) {
8974d03abcd49af490dba3850d341b955dd72f24959Robin Lee                    addUserToRanges(ranges, user.id, allowedApplications, disallowedApplications);
8984d03abcd49af490dba3850d341b955dd72f24959Robin Lee                }
8994d03abcd49af490dba3850d341b955dd72f24959Robin Lee            }
900c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
9014d03abcd49af490dba3850d341b955dd72f24959Robin Lee        return ranges;
9024d03abcd49af490dba3850d341b955dd72f24959Robin Lee    }
90369887e838814642a7ae78fc810656c7c8afc2a19Robert Greenwalt
9044d03abcd49af490dba3850d341b955dd72f24959Robin Lee    /**
9054d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * Updates a {@link Set} of non-intersecting {@link UidRange} objects to include all UIDs
9064d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * associated with one user.
9074d03abcd49af490dba3850d341b955dd72f24959Robin Lee     *
9084d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * <p>If one of {@param allowedApplications} or {@param disallowedApplications} is provided,
9094d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * the UID ranges will match the app whitelist or blacklist specified there. Otherwise, all UIDs
9104d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * in the user will be included.
9114d03abcd49af490dba3850d341b955dd72f24959Robin Lee     *
9124d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @param ranges {@link Set} of {@link UidRange}s to which to add.
9134d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @param userHandle The userId to add to {@param ranges}.
9144d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @param allowedApplications (optional) whitelist of applications to include.
9154d03abcd49af490dba3850d341b955dd72f24959Robin Lee     * @param disallowedApplications (optional) blacklist of applications to exclude.
9164d03abcd49af490dba3850d341b955dd72f24959Robin Lee     */
9174d03abcd49af490dba3850d341b955dd72f24959Robin Lee    @VisibleForTesting
9184d03abcd49af490dba3850d341b955dd72f24959Robin Lee    void addUserToRanges(@NonNull Set<UidRange> ranges, @UserIdInt int userHandle,
9194d03abcd49af490dba3850d341b955dd72f24959Robin Lee            @Nullable List<String> allowedApplications,
9204d03abcd49af490dba3850d341b955dd72f24959Robin Lee            @Nullable List<String> disallowedApplications) {
9214d03abcd49af490dba3850d341b955dd72f24959Robin Lee        if (allowedApplications != null) {
9220784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            // Add ranges covering all UIDs for allowedApplications.
9230784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            int start = -1, stop = -1;
9244d03abcd49af490dba3850d341b955dd72f24959Robin Lee            for (int uid : getAppsUids(allowedApplications, userHandle)) {
9250784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                if (start == -1) {
9260784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    start = uid;
9270784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                } else if (uid != stop + 1) {
9284d03abcd49af490dba3850d341b955dd72f24959Robin Lee                    ranges.add(new UidRange(start, stop));
9290784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    start = uid;
9300784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                }
9310784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                stop = uid;
9320784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            }
9334d03abcd49af490dba3850d341b955dd72f24959Robin Lee            if (start != -1) ranges.add(new UidRange(start, stop));
9344d03abcd49af490dba3850d341b955dd72f24959Robin Lee        } else if (disallowedApplications != null) {
9350784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            // Add all ranges for user skipping UIDs for disallowedApplications.
9360784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            final UidRange userRange = UidRange.createForUser(userHandle);
9370784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            int start = userRange.start;
9384d03abcd49af490dba3850d341b955dd72f24959Robin Lee            for (int uid : getAppsUids(disallowedApplications, userHandle)) {
9390784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                if (uid == start) {
9400784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    start++;
9410784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                } else {
9424d03abcd49af490dba3850d341b955dd72f24959Robin Lee                    ranges.add(new UidRange(start, uid - 1));
9430784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                    start = uid + 1;
9440784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                }
9450784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            }
9464d03abcd49af490dba3850d341b955dd72f24959Robin Lee            if (start <= userRange.stop) ranges.add(new UidRange(start, userRange.stop));
9470784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        } else {
9480784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            // Add all UIDs for the user.
9494d03abcd49af490dba3850d341b955dd72f24959Robin Lee            ranges.add(UidRange.createForUser(userHandle));
9500784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        }
951c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
952c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
9530784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    // Returns the subset of the full list of active UID ranges the VPN applies to (mVpnUsers) that
9540784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    // apply to userHandle.
9550784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private List<UidRange> uidRangesForUser(int userHandle) {
9560784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        final UidRange userRange = UidRange.createForUser(userHandle);
9570784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        final List<UidRange> ranges = new ArrayList<UidRange>();
9580784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        for (UidRange range : mVpnUsers) {
9594d03abcd49af490dba3850d341b955dd72f24959Robin Lee            if (userRange.containsRange(range)) {
9600784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen                ranges.add(range);
9610784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            }
9620784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        }
9630784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        return ranges;
9640784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    }
9650784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen
9660784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen    private void removeVpnUserLocked(int userHandle) {
967c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (mVpnUsers == null) {
96890b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            throw new IllegalStateException("VPN is not active");
96990b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        }
9700784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        final List<UidRange> ranges = uidRangesForUser(userHandle);
97190b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        if (mNetworkAgent != null) {
9720784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen            mNetworkAgent.removeUidRanges(ranges.toArray(new UidRange[ranges.size()]));
97390b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        }
9740784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        mVpnUsers.removeAll(ranges);
975c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
976c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
9771c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov    public void onUserAdded(int userHandle) {
9781c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov        // If the user is restricted tie them to the parent user's VPN
9791c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov        UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
98017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
9811c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov            synchronized(Vpn.this) {
98217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                if (mVpnUsers != null) {
98317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    try {
98417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                        addUserToRanges(mVpnUsers, userHandle, mConfig.allowedApplications,
98517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                                mConfig.disallowedApplications);
98617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                        if (mNetworkAgent != null) {
98717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                            final List<UidRange> ranges = uidRangesForUser(userHandle);
98817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                            mNetworkAgent.addUidRanges(ranges.toArray(new UidRange[ranges.size()]));
98917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                        }
99017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    } catch (Exception e) {
99117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                        Log.wtf(TAG, "Failed to add restricted user to owner", e);
9926bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    }
99317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                }
99417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                if (mAlwaysOn) {
99517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    setVpnForcedLocked(mLockdown);
996c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                }
997c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
998c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
999c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
1000c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
10011c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov    public void onUserRemoved(int userHandle) {
1002c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        // clean up if restricted
10031c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov        UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
100417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
10051c36315a36962321dfe870b07e28b04a1d6777e9Fyodor Kupolov            synchronized(Vpn.this) {
100617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                if (mVpnUsers != null) {
100717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    try {
100817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                        removeVpnUserLocked(userHandle);
100917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    } catch (Exception e) {
101017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                        Log.wtf(TAG, "Failed to remove restricted user to owner", e);
101117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    }
101217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                }
101317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                if (mAlwaysOn) {
101417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    setVpnForcedLocked(mLockdown);
1015c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker                }
1016c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            }
1017c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        }
1018c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker    }
1019c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker
1020bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker    /**
102117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * Called when the user associated with this VPN has just been stopped.
102217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     */
102317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    public synchronized void onUserStopped() {
102417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        // Switch off networking lockdown (if it was enabled)
102517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        setVpnForcedLocked(false);
102617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        mAlwaysOn = false;
102717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
1028812800cb92090db31f609b907c4458ba76cf7f42Robin Lee        unregisterPackageChangeReceiverLocked();
102917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        // Quit any active connections
103017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        agentDisconnect();
103117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    }
103217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
103317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    /**
103417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * Restrict network access from all UIDs affected by this {@link Vpn}, apart from the VPN
103517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * service app itself, to only sockets that have had {@code protect()} called on them. All
103617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * non-VPN traffic is blocked via a {@code PROHIBIT} response from the kernel.
103717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *
103817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * The exception for the VPN UID isn't technically necessary -- setup should use protected
103917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * sockets -- but in practice it saves apps that don't protect their sockets from breaking.
104017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *
104117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * Calling multiple times with {@param enforce} = {@code true} will recreate the set of UIDs to
104217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * block every time, and if anything has changed update using {@link #setAllowOnlyVpnForUids}.
104317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *
104417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @param enforce {@code true} to require that all traffic under the jurisdiction of this
104517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *                {@link Vpn} goes through a VPN connection or is blocked until one is
104617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *                available, {@code false} to lift the requirement.
104717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *
104817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @see #mBlockedUsers
104917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     */
105017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    @GuardedBy("this")
105117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    private void setVpnForcedLocked(boolean enforce) {
105217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        final Set<UidRange> removedRanges = new ArraySet<>(mBlockedUsers);
105317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        if (enforce) {
105417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            final Set<UidRange> addedRanges = createUserAndRestrictedProfilesRanges(mUserHandle,
105517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    /* allowedApplications */ null,
105617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    /* disallowedApplications */ Collections.singletonList(mPackage));
105717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
105817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            removedRanges.removeAll(addedRanges);
105917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            addedRanges.removeAll(mBlockedUsers);
106017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
106117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            setAllowOnlyVpnForUids(false, removedRanges);
106217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            setAllowOnlyVpnForUids(true, addedRanges);
106317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        } else {
106417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            setAllowOnlyVpnForUids(false, removedRanges);
106517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        }
106617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    }
106717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
106817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    /**
106917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * Either add or remove a list of {@link UidRange}s to the list of UIDs that are only allowed
107017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * to make connections through sockets that have had {@code protect()} called on them.
107117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *
107217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @param enforce {@code true} to add to the blacklist, {@code false} to remove.
107317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @param ranges {@link Collection} of {@link UidRange}s to add (if {@param enforce} is
107417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *               {@code true}) or to remove.
107517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @return {@code true} if all of the UIDs were added/removed. {@code false} otherwise,
107617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *         including added ranges that already existed or removed ones that didn't.
107717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     */
107817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    @GuardedBy("this")
107917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    private boolean setAllowOnlyVpnForUids(boolean enforce, Collection<UidRange> ranges) {
108017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        if (ranges.size() == 0) {
108117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            return true;
108217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        }
108317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        final UidRange[] rangesArray = ranges.toArray(new UidRange[ranges.size()]);
108417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        try {
108517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            mNetd.setAllowOnlyVpnForUids(enforce, rangesArray);
108617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        } catch (RemoteException | RuntimeException e) {
108717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            Log.e(TAG, "Updating blocked=" + enforce
108817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee                    + " for UIDs " + Arrays.toString(ranges.toArray()) + " failed", e);
108917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            return false;
109017e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        }
109117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        if (enforce) {
109217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            mBlockedUsers.addAll(ranges);
109317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        } else {
109417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            mBlockedUsers.removeAll(ranges);
109517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        }
109617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        return true;
109717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    }
109817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
109917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    /**
1100bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker     * Return the configuration of the currently running VPN.
1101bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker     */
1102bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker    public VpnConfig getVpnConfig() {
1103bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker        enforceControlPermission();
1104bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker        return mConfig;
1105bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker    }
1106bf6ff2c025405a3af496fe558dfc4468a9b45cc8Chad Brubaker
1107899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    @Deprecated
1108899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    public synchronized void interfaceStatusChanged(String iface, boolean up) {
1109899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        try {
1110899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            mObserver.interfaceStatusChanged(iface, up);
1111899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        } catch (RemoteException e) {
1112899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            // ignored; target is local
1113aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        }
1114ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh    }
1115ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
1116899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
1117899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        @Override
1118899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        public void interfaceStatusChanged(String interfaze, boolean up) {
1119899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            synchronized (Vpn.this) {
1120899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                if (!up && mLegacyVpnRunner != null) {
1121899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    mLegacyVpnRunner.check(interfaze);
1122899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
1123199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            }
1124ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh        }
1125ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh
1126899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        @Override
1127899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        public void interfaceRemoved(String interfaze) {
1128899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            synchronized (Vpn.this) {
1129899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
113090b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson                    mStatusIntent = null;
11316bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    mVpnUsers = null;
1132c4c7231eb6d1efa9ecd7b693f8328a76a04e8bbbPaul Jensen                    mConfig = null;
1133899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    mInterface = null;
1134899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    if (mConnection != null) {
1135899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mContext.unbindService(mConnection);
1136899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mConnection = null;
11376bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                        agentDisconnect();
1138899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    } else if (mLegacyVpnRunner != null) {
1139899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mLegacyVpnRunner.exit();
1140899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                        mLegacyVpnRunner = null;
1141899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    }
1142899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
1143899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
1144899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
1145899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    };
1146db3c8678e5cbdfec011afaf25bde2091152c30adHaoyu Bai
1147dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh    private void enforceControlPermission() {
1148bc19c181c8c058c824e4fee907a05129e142c388Jeff Davidson        mContext.enforceCallingPermission(Manifest.permission.CONTROL_VPN, "Unauthorized Caller");
1149dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh    }
1150dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh
1151244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee    private void enforceControlPermissionOrInternalCaller() {
1152244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        // Require caller to be either an application with CONTROL_VPN permission or a process
1153244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        // in the system server.
1154244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee        mContext.enforceCallingOrSelfPermission(Manifest.permission.CONTROL_VPN,
1155244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee                "Unauthorized Caller");
1156244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee    }
1157244ce8ef5f201cf403bab43df8281671a9e94512Robin Lee
1158199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    private class Connection implements ServiceConnection {
1159199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        private IBinder mService;
1160199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
1161199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        @Override
1162199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        public void onServiceConnected(ComponentName name, IBinder service) {
1163199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mService = service;
1164199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
1165199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
1166199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        @Override
1167199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        public void onServiceDisconnected(ComponentName name) {
1168199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh            mService = null;
1169199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh        }
1170199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh    }
1171199ed6ef89bd356895534ba09ac43ed340cd9a1aChia-chi Yeh
117290b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson    private void prepareStatusIntent() {
117390b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        final long token = Binder.clearCallingIdentity();
117490b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        try {
117590b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
117690b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        } finally {
117790b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            Binder.restoreCallingIdentity(token);
117890b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        }
117990b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson    }
118090b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson
1181f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    public synchronized boolean addAddress(String address, int prefixLength) {
1182c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (!isCallerEstablishedOwnerLocked()) {
1183f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran            return false;
1184f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        }
1185f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        boolean success = jniAddAddress(mInterface, address, prefixLength);
1186c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        mNetworkAgent.sendLinkProperties(makeLinkProperties());
1187f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        return success;
1188f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    }
1189f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran
1190f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    public synchronized boolean removeAddress(String address, int prefixLength) {
1191c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (!isCallerEstablishedOwnerLocked()) {
1192f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran            return false;
1193f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        }
1194f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        boolean success = jniDelAddress(mInterface, address, prefixLength);
1195c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        mNetworkAgent.sendLinkProperties(makeLinkProperties());
1196f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran        return success;
1197f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    }
1198f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran
1199c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    public synchronized boolean setUnderlyingNetworks(Network[] networks) {
1200c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (!isCallerEstablishedOwnerLocked()) {
1201c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            return false;
1202c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        }
1203c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (networks == null) {
1204c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            mConfig.underlyingNetworks = null;
1205c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        } else {
1206c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            mConfig.underlyingNetworks = new Network[networks.length];
1207c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            for (int i = 0; i < networks.length; ++i) {
1208c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                if (networks[i] == null) {
1209c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                    mConfig.underlyingNetworks[i] = null;
1210c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                } else {
1211c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                    mConfig.underlyingNetworks[i] = new Network(networks[i].netId);
1212c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                }
1213c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            }
1214c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        }
1215c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        return true;
1216c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    }
1217c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran
1218c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    public synchronized Network[] getUnderlyingNetworks() {
1219c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (!isRunningLocked()) {
1220c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            return null;
1221c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        }
1222c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        return mConfig.underlyingNetworks;
1223c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    }
1224c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran
1225f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong    /**
1226f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong     * This method should only be called by ConnectivityService. Because it doesn't
1227f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong     * have enough data to fill VpnInfo.primaryUnderlyingIface field.
1228f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong     */
1229f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong    public synchronized VpnInfo getVpnInfo() {
1230f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        if (!isRunningLocked()) {
1231f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong            return null;
1232f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        }
1233f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong
1234f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        VpnInfo info = new VpnInfo();
1235f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        info.ownerUid = mOwnerUID;
1236f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        info.vpnIface = mInterface;
1237f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong        return info;
1238f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong    }
1239f5ea340aabee6e290448c8cc9fb0925da8b7db5eWenchao Tong
1240c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    public synchronized boolean appliesToUid(int uid) {
1241c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        if (!isRunningLocked()) {
1242c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            return false;
1243c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        }
1244c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        for (UidRange uidRange : mVpnUsers) {
12454d03abcd49af490dba3850d341b955dd72f24959Robin Lee            if (uidRange.contains(uid)) {
1246c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran                return true;
1247c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran            }
1248c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        }
1249c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran        return false;
1250c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran    }
1251c2c0beab79a907f63e109eefe2a5aabcf2e3fd8fSreeram Ramachandran
125217e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    /**
1253ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee     * @return {@code true} if {@param uid} is blocked by an always-on VPN.
1254ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee     *         A UID is blocked if it's included in one of the mBlockedUsers ranges and the VPN is
1255ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee     *         not connected, or if the VPN is connected but does not apply to the UID.
125617e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     *
125717e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     * @see #mBlockedUsers
125817e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee     */
125917e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    public synchronized boolean isBlockingUid(int uid) {
1260ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee        if (!mLockdown) {
1261ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee            return false;
1262ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee        }
1263ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee
1264ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee        if (mNetworkInfo.isConnected()) {
1265ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee            return !appliesToUid(uid);
1266ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee        } else {
1267ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee            for (UidRange uidRange : mBlockedUsers) {
1268ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee                if (uidRange.contains(uid)) {
1269ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee                    return true;
1270ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee                }
127117e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee            }
1272ebbcb54a4380239ea3d0c4d1a20cd6b3c9ec0590Robin Lee            return false;
127317e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee        }
127417e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee    }
127517e6183b85ba3038acb935aaa01415058b2e6dddRobin Lee
12761a405fe300950d6ceae2166fd074b596d8110dbeTony Mak    private void updateAlwaysOnNotification(DetailedState networkState) {
12771a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        final boolean visible = (mAlwaysOn && networkState != DetailedState.CONNECTED);
12781a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        updateAlwaysOnNotificationInternal(visible);
12791a405fe300950d6ceae2166fd074b596d8110dbeTony Mak    }
12801a405fe300950d6ceae2166fd074b596d8110dbeTony Mak
12811a405fe300950d6ceae2166fd074b596d8110dbeTony Mak    @VisibleForTesting
12821a405fe300950d6ceae2166fd074b596d8110dbeTony Mak    protected void updateAlwaysOnNotificationInternal(boolean visible) {
12831a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        final UserHandle user = UserHandle.of(mUserHandle);
12841a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        final long token = Binder.clearCallingIdentity();
12851a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        try {
12861a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            final NotificationManager notificationManager = NotificationManager.from(mContext);
12871a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            if (!visible) {
12881a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                notificationManager.cancelAsUser(TAG, 0, user);
12891a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                return;
12901a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            }
12911a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            final Intent intent = new Intent(Settings.ACTION_VPN_SETTINGS);
12921a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            final PendingIntent configIntent = PendingIntent.getActivityAsUser(
12931a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                    mContext, /* request */ 0, intent,
12941a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                    PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
12951a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                    null, user);
12961a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            final Notification.Builder builder = new Notification.Builder(mContext)
12971a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                    .setDefaults(0)
12981a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                    .setSmallIcon(R.drawable.vpn_connected)
12991a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                    .setContentTitle(mContext.getString(R.string.vpn_lockdown_disconnected))
13001a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                    .setContentText(mContext.getString(R.string.vpn_lockdown_config))
13011a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                    .setContentIntent(configIntent)
13021a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                    .setCategory(Notification.CATEGORY_SYSTEM)
13031a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                    .setPriority(Notification.PRIORITY_LOW)
13041a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                    .setVisibility(Notification.VISIBILITY_PUBLIC)
13051a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                    .setOngoing(true)
13061a405fe300950d6ceae2166fd074b596d8110dbeTony Mak                    .setColor(mContext.getColor(R.color.system_notification_accent_color));
13071a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            notificationManager.notifyAsUser(TAG, 0, builder.build(), user);
13081a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        } finally {
13091a405fe300950d6ceae2166fd074b596d8110dbeTony Mak            Binder.restoreCallingIdentity(token);
13101a405fe300950d6ceae2166fd074b596d8110dbeTony Mak        }
13111a405fe300950d6ceae2166fd074b596d8110dbeTony Mak    }
13121a405fe300950d6ceae2166fd074b596d8110dbeTony Mak
131397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh    private native int jniCreate(int mtu);
1314c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native String jniGetName(int tun);
131597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh    private native int jniSetAddresses(String interfaze, String addresses);
1316c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native void jniReset(String interfaze);
1317c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh    private native int jniCheck(String interfaze);
1318f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    private native boolean jniAddAddress(String interfaze, String address, int prefixLen);
1319f4e0c0cb8ef22fdb20ae74b444c9f4b7d15ded8bSreeram Ramachandran    private native boolean jniDelAddress(String interfaze, String address, int prefixLen);
132085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
132141fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti    private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
132241fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        for (RouteInfo route : prop.getAllRoutes()) {
132382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            // Currently legacy VPN only works on IPv4.
132482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
132541fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti                return route;
132682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            }
132782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
132882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
132941fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        throw new IllegalStateException("Unable to find IPv4 default gateway");
133082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    }
133182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
133285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    /**
133382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey     * Start legacy VPN, controlling native daemons as needed. Creates a
133482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey     * secondary thread to perform connection work, returning quickly.
1335b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     *
1336b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * Should only be called to respond to Binder requests as this enforces caller permission. Use
1337b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, LinkProperties)} to skip the
1338b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * permission check only when the caller is trusted (or the call is initiated by the system).
133985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     */
134082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
13415a6bdc46e2fdc8cfd930396773dd89efd19fa1f1Robert Greenwalt        enforceControlPermission();
1342b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        long token = Binder.clearCallingIdentity();
1343b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        try {
1344b21298a686b04d55ff97223dd317497845713f4bJeff Davidson            startLegacyVpnPrivileged(profile, keyStore, egress);
1345b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        } finally {
1346b21298a686b04d55ff97223dd317497845713f4bJeff Davidson            Binder.restoreCallingIdentity(token);
1347b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        }
1348b21298a686b04d55ff97223dd317497845713f4bJeff Davidson    }
1349b21298a686b04d55ff97223dd317497845713f4bJeff Davidson
1350b21298a686b04d55ff97223dd317497845713f4bJeff Davidson    /**
1351b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * Like {@link #startLegacyVpn(VpnProfile, KeyStore, LinkProperties)}, but does not check
1352b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * permissions under the assumption that the caller is the system.
1353b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     *
1354b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     * Callers are responsible for checking permissions if needed.
1355b21298a686b04d55ff97223dd317497845713f4bJeff Davidson     */
1356b21298a686b04d55ff97223dd317497845713f4bJeff Davidson    public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
1357b21298a686b04d55ff97223dd317497845713f4bJeff Davidson            LinkProperties egress) {
1358f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds        UserManager mgr = UserManager.get(mContext);
13590784eeab28da094a87437ed454fe3dca01b1f9f2Paul Jensen        UserInfo user = mgr.getUserInfo(mUserHandle);
136095778ffc58979d19ff9f4aaed396a6eca49cf698Nicolas Prevot        if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
136195778ffc58979d19ff9f4aaed396a6eca49cf698Nicolas Prevot                    new UserHandle(mUserHandle))) {
1362f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds            throw new SecurityException("Restricted users cannot establish VPNs");
1363f5116d01b20f21ba32cd9eaa3412daf97f41c623Julia Reynolds        }
136482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
136541fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
136641fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
136741fb98c8681cbe0040b8d4efc65d33717c3819f6Lorenzo Colitti        final String iface = ipv4DefaultRoute.getInterface();
136882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
136982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        // Load certificates.
137082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String privateKey = "";
137182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String userCert = "";
137282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String caCert = "";
137382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String serverCert = "";
137482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.ipsecUserCert.isEmpty()) {
137582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
137682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
1377d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes            userCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
137882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
137982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.ipsecCaCert.isEmpty()) {
138082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
1381d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes            caCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
138282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
138382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.ipsecServerCert.isEmpty()) {
138482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
1385d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes            serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
138682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
138782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
138882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            throw new IllegalStateException("Cannot load credentials");
138982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
139082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
139182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        // Prepare arguments for racoon.
139282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String[] racoon = null;
139382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        switch (profile.type) {
139482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
139582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
139682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "udppsk", profile.ipsecIdentifier,
139782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    profile.ipsecSecret, "1701",
139882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
139982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
140082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
140182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
140282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "udprsa", privateKey, userCert,
140382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    caCert, serverCert, "1701",
140482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
140582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
140682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
140782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
140882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
140982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    profile.ipsecSecret, profile.username, profile.password, "", gateway,
141082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
141182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
141282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
141382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
141482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "xauthrsa", privateKey, userCert,
141582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    caCert, serverCert, profile.username, profile.password, "", gateway,
141682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
141782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
141882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
141982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                racoon = new String[] {
142082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, profile.server, "hybridrsa",
142182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    caCert, serverCert, profile.username, profile.password, "", gateway,
142282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
142382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
142482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
142582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
142682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        // Prepare arguments for mtpd.
142782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        String[] mtpd = null;
142882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        switch (profile.type) {
142982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_PPTP:
143082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                mtpd = new String[] {
143182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, "pptp", profile.server, "1723",
143282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "name", profile.username, "password", profile.password,
143382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
143482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
143582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    (profile.mppe ? "+mppe" : "nomppe"),
143682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
143782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
143882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
143982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
144082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                mtpd = new String[] {
144182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
144282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "name", profile.username, "password", profile.password,
144382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
144482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
144582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                };
144682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey                break;
144782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
1448899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
144982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        VpnConfig config = new VpnConfig();
1450899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        config.legacy = true;
145182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        config.user = profile.key;
145282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        config.interfaze = iface;
145382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        config.session = profile.name;
14544ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker
14554ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker        config.addLegacyRoutes(profile.routes);
145682f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.dnsServers.isEmpty()) {
145782f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
145882f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
145982f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        if (!profile.searchDomains.isEmpty()) {
146082f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey            config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
146182f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        }
146282f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey        startLegacyVpn(config, racoon, mtpd);
146382f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    }
146482f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey
146582f8521d386f3109147c477d04e5e90e5c715fa0Jeff Sharkey    private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
1466b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        stopLegacyVpnPrivileged();
1467899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1468b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        // Prepare for the new request.
1469b21298a686b04d55ff97223dd317497845713f4bJeff Davidson        prepareInternal(VpnConfig.LEGACY_VPN);
1470899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        updateState(DetailedState.CONNECTING, "startLegacyVpn");
147185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
14722e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        // Start a new LegacyVpnRunner and we are done!
1473100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
1474100155a310fbb5028fc48e359bdfb7c4d3531843Chia-chi Yeh        mLegacyVpnRunner.start();
147585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    }
147685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
1477b21298a686b04d55ff97223dd317497845713f4bJeff Davidson    /** Stop legacy VPN. Permissions must be checked by callers. */
1478b21298a686b04d55ff97223dd317497845713f4bJeff Davidson    public synchronized void stopLegacyVpnPrivileged() {
1479899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        if (mLegacyVpnRunner != null) {
1480899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            mLegacyVpnRunner.exit();
1481899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            mLegacyVpnRunner = null;
1482899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1483899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            synchronized (LegacyVpnRunner.TAG) {
1484899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                // wait for old thread to completely finish before spinning up
1485899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                // new instance, otherwise state updates can be out of order.
1486899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
1487899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
1488899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey    }
1489899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
149085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    /**
14912e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     * Return the information of the current ongoing legacy VPN.
14922e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh     */
14932e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    public synchronized LegacyVpnInfo getLegacyVpnInfo() {
1494dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        // Check if the caller is authorized.
1495dadc857d9de364fded10d4f69eb82bc9cd35d4b7Chia-chi Yeh        enforceControlPermission();
149608bbca040fa921b99493cd9967453ed90b1b710asj.cha        return getLegacyVpnInfoPrivileged();
149708bbca040fa921b99493cd9967453ed90b1b710asj.cha    }
149808bbca040fa921b99493cd9967453ed90b1b710asj.cha
149908bbca040fa921b99493cd9967453ed90b1b710asj.cha    /**
150008bbca040fa921b99493cd9967453ed90b1b710asj.cha     * Return the information of the current ongoing legacy VPN.
150108bbca040fa921b99493cd9967453ed90b1b710asj.cha     * Callers are responsible for checking permissions if needed.
150208bbca040fa921b99493cd9967453ed90b1b710asj.cha     */
150308bbca040fa921b99493cd9967453ed90b1b710asj.cha    public synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() {
1504899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        if (mLegacyVpnRunner == null) return null;
1505899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1506899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        final LegacyVpnInfo info = new LegacyVpnInfo();
1507c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker        info.key = mConfig.user;
1508899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
150990b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        if (mNetworkInfo.isConnected()) {
151090b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson            info.intent = mStatusIntent;
151190b1b9f985a91fb54254705515f822b09c68ac26Jeff Davidson        }
1512899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        return info;
15132e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    }
15142e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
151569ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    public VpnConfig getLegacyVpnConfig() {
151669ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        if (mLegacyVpnRunner != null) {
1517c2865195b66490bd1f9d3df4fe4f5e2a46e2196aChad Brubaker            return mConfig;
151869ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        } else {
151969ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey            return null;
152069ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey        }
152169ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey    }
152269ddab4575ff684c533c995e07ca15fe18543fc0Jeff Sharkey
15232e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh    /**
152485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * Bringing up a VPN connection takes time, and that is all this thread
152585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * does. Here we have plenty of time. The only thing we need to take
152685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * care of is responding to interruptions as soon as possible. Otherwise
152785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * requests will be piled up. This can be done in a Handler as a state
152885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     * machine, but it is much easier to read in the current form.
152985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh     */
153085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    private class LegacyVpnRunner extends Thread {
153185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private static final String TAG = "LegacyVpnRunner";
153285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
15331f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh        private final String[] mDaemons;
153485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private final String[][] mArguments;
15355317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh        private final LocalSocket[] mSockets;
153653c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt        private final String mOuterInterface;
15371b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        private final AtomicInteger mOuterConnection =
15381b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                new AtomicInteger(ConnectivityManager.TYPE_NONE);
15392e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
154085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private long mTimer = -1;
154185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
15421b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        /**
15431b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt         * Watch for the outer connection (passing in the constructor) going away.
15441b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt         */
15451b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
15461b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            @Override
15471b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            public void onReceive(Context context, Intent intent) {
154857666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey                if (!mEnableTeardown) return;
154957666934b4a161cc2fb77eef15d0b894aaf8f173Jeff Sharkey
15501b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
15511b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                    if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
15521b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                            ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
15531b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        NetworkInfo info = (NetworkInfo)intent.getExtra(
15541b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                                ConnectivityManager.EXTRA_NETWORK_INFO);
15551b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        if (info != null && !info.isConnectedOrConnecting()) {
15561b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                            try {
15571b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                                mObserver.interfaceStatusChanged(mOuterInterface, false);
15581b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                            } catch (RemoteException e) {}
15591b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                        }
15601b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                    }
15611b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                }
15621b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            }
15631b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt        };
15641b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt
156541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
156685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            super(TAG);
156741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mConfig = config;
156841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mDaemons = new String[] {"racoon", "mtpd"};
1569899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            // TODO: clear arguments from memory once launched
157041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh            mArguments = new String[][] {racoon, mtpd};
15715317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh            mSockets = new LocalSocket[mDaemons.length];
157253c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt
157353c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            // This is the interface which VPN is running on,
157453c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            // mConfig.interfaze will change to point to OUR
157553c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            // internal interface soon. TODO - add inner/outer to mconfig
15761b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
15774ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker            // we will leave the VPN up.  We should check that it's still there/connected after
15781b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            // registering
157953c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            mOuterInterface = mConfig.interfaze;
15801b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt
1581e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen            if (!TextUtils.isEmpty(mOuterInterface)) {
1582e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                final ConnectivityManager cm = ConnectivityManager.from(mContext);
1583e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                for (Network network : cm.getAllNetworks()) {
1584e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                    final LinkProperties lp = cm.getLinkProperties(network);
15851b60d11b8f54f1ade45b80668601bc955041cf4fLorenzo Colitti                    if (lp != null && lp.getAllInterfaceNames().contains(mOuterInterface)) {
1586e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                        final NetworkInfo networkInfo = cm.getNetworkInfo(network);
1587e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                        if (networkInfo != null) mOuterConnection.set(networkInfo.getType());
1588e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                    }
1589e75b9e355500b7c6a05e4d6ec54ef48835707caaPaul Jensen                }
15901b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            }
15911b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt
15921b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            IntentFilter filter = new IntentFilter();
15931b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
15941b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            mContext.registerReceiver(mBroadcastReceiver, filter);
159541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        }
159641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
1597aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        public void check(String interfaze) {
159853c04bdd35a85aa65d1a1f18ca2ee34970e2c2d0Robert Greenwalt            if (interfaze.equals(mOuterInterface)) {
1599aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh                Log.i(TAG, "Legacy VPN is going down with " + interfaze);
1600aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh                exit();
1601aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh            }
1602aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh        }
1603aa1727fe0cbb902c5f53a3fae601b4e15da0a2f4Chia-chi Yeh
160441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh        public void exit() {
16055317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh            // We assume that everything is reset after stopping the daemons.
160697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh            interrupt();
16076bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen            agentDisconnect();
16081b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            try {
16091b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt                mContext.unregisterReceiver(mBroadcastReceiver);
16101b0ca9dace3fb3b84f8a87e539c0179e6093b423Robert Greenwalt            } catch (IllegalArgumentException e) {}
16112e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh        }
16122e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
161385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        @Override
161485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        public void run() {
161585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            // Wait for the previous thread since it has been interrupted.
16162e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh            Log.v(TAG, "Waiting");
161785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            synchronized (TAG) {
16182e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                Log.v(TAG, "Executing");
1619047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                try {
1620047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    execute();
1621047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    monitorDaemons();
1622047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    interrupted(); // Clear interrupt flag if execute called exit.
1623047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                } catch (InterruptedException e) {
1624047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                } finally {
1625047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    for (LocalSocket socket : mSockets) {
1626047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                        IoUtils.closeQuietly(socket);
1627047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    }
1628047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    // This sleep is necessary for racoon to successfully complete sending delete
1629047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    // message to server.
1630047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    try {
1631047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                        Thread.sleep(50);
1632047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    } catch (InterruptedException e) {
1633047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    }
1634047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    for (String daemon : mDaemons) {
1635047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                        SystemService.stop(daemon);
1636047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    }
1637047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                }
1638047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                agentDisconnect();
163985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
164085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
164185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
164285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private void checkpoint(boolean yield) throws InterruptedException {
164385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            long now = SystemClock.elapsedRealtime();
164485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            if (mTimer == -1) {
164585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                mTimer = now;
164685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Thread.sleep(1);
16477ef8611b5f3a893a46c7b9e22bdd8ab252e373ffChia-chi Yeh            } else if (now - mTimer <= 60000) {
164885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                Thread.sleep(yield ? 200 : 1);
164985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } else {
1650899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                updateState(DetailedState.FAILED, "checkpoint");
165197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                throw new IllegalStateException("Time is up");
165285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
165385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
165485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
165585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        private void execute() {
165685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            // Catch all exceptions so we can clean up few things.
1657899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            boolean initFinished = false;
165885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            try {
165985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                // Initialize the timer.
166085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                checkpoint(false);
166185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
16621f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                // Wait for the daemons to stop.
16631f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                for (String daemon : mDaemons) {
1664088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                    while (!SystemService.isStopped(daemon)) {
166585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
166685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
166785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
166885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
166997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Clear the previous state.
167097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                File state = new File("/data/misc/vpn/state");
167197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                state.delete();
167297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (state.exists()) {
167397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    throw new IllegalStateException("Cannot delete the state");
167485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
1675c1872732922214de80f790e14865e41dd1b98203Chia-chi Yeh                new File("/data/misc/vpn/abort").delete();
1676899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                initFinished = true;
167785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
1678e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                // Check if we need to restart any of the daemons.
167985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                boolean restart = false;
168085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                for (String[] arguments : mArguments) {
168185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    restart = restart || (arguments != null);
168285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
168385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                if (!restart) {
16846bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    agentDisconnect();
168585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    return;
168685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
1687899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                updateState(DetailedState.CONNECTING, "execute");
168885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
16891f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                // Start the daemon with arguments.
16901f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                for (int i = 0; i < mDaemons.length; ++i) {
169185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    String[] arguments = mArguments[i];
169285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    if (arguments == null) {
169385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        continue;
169485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
169585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
16961f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Start the daemon.
16971f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    String daemon = mDaemons[i];
1698088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                    SystemService.start(daemon);
169985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
17001f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Wait for the daemon to start.
1701088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                    while (!SystemService.isRunning(daemon)) {
170285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
170385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
170485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
170585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Create the control socket.
17065317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    mSockets[i] = new LocalSocket();
170785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    LocalSocketAddress address = new LocalSocketAddress(
17081f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                            daemon, LocalSocketAddress.Namespace.RESERVED);
170985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
171085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Wait for the socket to connect.
171185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    while (true) {
171285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        try {
17135317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                            mSockets[i].connect(address);
171485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            break;
171585a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        } catch (Exception e) {
171685a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                            // ignore
171785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
171885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(true);
171985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
17205317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    mSockets[i].setSoTimeout(500);
172185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
172285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    // Send over the arguments.
17235317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    OutputStream out = mSockets[i].getOutputStream();
172485a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    for (String argument : arguments) {
1725d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes                        byte[] bytes = argument.getBytes(StandardCharsets.UTF_8);
17265317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                        if (bytes.length >= 0xFFFF) {
172797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            throw new IllegalArgumentException("Argument is too large");
172885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
17291f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes.length >> 8);
17301f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes.length);
17311f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        out.write(bytes);
173285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        checkpoint(false);
173385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
17345317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    out.write(0xFF);
17355317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    out.write(0xFF);
17361f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    out.flush();
173797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
173897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    // Wait for End-of-File.
17395317f034bacaab19af3181da8e9752cbb5b09a08Chia-chi Yeh                    InputStream in = mSockets[i].getInputStream();
174097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    while (true) {
174197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        try {
174297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            if (in.read() == -1) {
174397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                                break;
174497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            }
174597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        } catch (Exception e) {
174697a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                            // ignore
174797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        }
174897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        checkpoint(true);
174997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    }
175085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
175185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
175297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Wait for the daemons to create the new state.
175397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                while (!state.exists()) {
17541f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    // Check if a running daemon is dead.
17551f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                    for (int i = 0; i < mDaemons.length; ++i) {
17561f7746b39b94be1149228751e45a40ea39603611Chia-chi Yeh                        String daemon = mDaemons[i];
1757088f29f55eebc6862a4cb5dddeaefacf24f74d95Jeff Sharkey                        if (mArguments[i] != null && !SystemService.isRunning(daemon)) {
17582e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                            throw new IllegalStateException(daemon + " is dead");
175985a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                        }
176085a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    }
176185a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                    checkpoint(true);
176285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh                }
176385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
176497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Now we are connected. Read and parse the new state.
1765c1bac3a6e240c1c9a14a7b515f585977fb908930Chia-chi Yeh                String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
17665026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                if (parameters.length != 7) {
176797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    throw new IllegalStateException("Cannot parse the state");
176897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
176997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
177097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the interface and the addresses in the config.
177197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                mConfig.interfaze = parameters[0].trim();
177285a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh
17734ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                mConfig.addLegacyAddresses(parameters[1]);
177497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the routes if they are not set in the config.
177597a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (mConfig.routes == null || mConfig.routes.isEmpty()) {
17764ca19e8377f33e8a80684fb4ee67f5a4bdc9ea76Chad Brubaker                    mConfig.addLegacyRoutes(parameters[2]);
177797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
177897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
177997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the DNS servers if they are not set in the config.
178041d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
178197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    String dnsServers = parameters[3].trim();
178241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    if (!dnsServers.isEmpty()) {
178341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                        mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
178441d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    }
178541d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                }
178641d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
178797a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Set the search domains if they are not set in the config.
178897a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
178997a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    String searchDomains = parameters[4].trim();
179097a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    if (!searchDomains.isEmpty()) {
179197a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                        mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
179297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                    }
179397a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                }
179497a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh
17955026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                // Add a throw route for the VPN server endpoint, if one was specified.
17965026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                String endpoint = parameters[5];
17975026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                if (!endpoint.isEmpty()) {
17985026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                    try {
17995026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        InetAddress addr = InetAddress.parseNumericAddress(endpoint);
18005026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        if (addr instanceof Inet4Address) {
18015026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                            mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 32), RTN_THROW));
18025026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        } else if (addr instanceof Inet6Address) {
18035026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                            mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 128), RTN_THROW));
18045026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        } else {
18055026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                            Log.e(TAG, "Unknown IP address family for VPN endpoint: " + endpoint);
18065026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        }
18075026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                    } catch (IllegalArgumentException e) {
18085026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                        Log.e(TAG, "Exception constructing throw route to " + endpoint + ": " + e);
18095026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                    }
18105026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti                }
18115026279ce45ae78126046607a2634dc9dae93199Lorenzo Colitti
181297a61565ea95472e65899070e64853f8c147bb11Chia-chi Yeh                // Here is the last step and it must be done synchronously.
181341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                synchronized (Vpn.this) {
18142b862e5e75ad419f17a4cea185b9349e0da70e7bVinit Deshapnde                    // Set the start time
18152b862e5e75ad419f17a4cea185b9349e0da70e7bVinit Deshapnde                    mConfig.startTime = SystemClock.elapsedRealtime();
18162b862e5e75ad419f17a4cea185b9349e0da70e7bVinit Deshapnde
181741d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    // Check if the thread is interrupted while we are waiting.
181841d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    checkpoint(false);
181941d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh
1820e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                    // Check if the interface is gone while we are waiting.
1821c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                    if (jniCheck(mConfig.interfaze) == 0) {
182234e7813e962de99df9813014678ef5901227c5f1Chia-chi Yeh                        throw new IllegalStateException(mConfig.interfaze + " is gone");
182341d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                    }
1824e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh
1825e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                    // Now INetworkManagementEventObserver is watching our back.
1826c2b8aa0b4c822b0e307f62131650f4a6ee89bb66Chia-chi Yeh                    mInterface = mConfig.interfaze;
18274d03abcd49af490dba3850d341b955dd72f24959Robin Lee                    prepareStatusIntent();
18286bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen
18296bc2c2c34f2b23eae79ad733c97a691734055c4fPaul Jensen                    agentConnect();
18302e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh
18312e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                    Log.i(TAG, "Connected!");
183241d1685a22ba8038517d6fdb57006023e03f12e1Chia-chi Yeh                }
183385a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            } catch (Exception e) {
18342e46764a707bd14cad22bc179669eeecb2d7c647Chia-chi Yeh                Log.i(TAG, "Aborting", e);
1835438406092ed71c658bf5a4e6ae2e7282fc4fab4dLorenzo Colitti                updateState(DetailedState.FAILED, e.getMessage());
1836e9107901ae264de4ff5603d3cfc63a03ca4117d4Chia-chi Yeh                exit();
183785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh            }
183885a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh        }
1839899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey
1840899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        /**
1841899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey         * Monitor the daemons we started, moving to disconnected state if the
1842899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey         * underlying services fail.
1843899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey         */
1844047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe        private void monitorDaemons() throws InterruptedException{
1845899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            if (!mNetworkInfo.isConnected()) {
1846899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                return;
1847899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
1848047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe            while (true) {
1849047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                Thread.sleep(2000);
1850047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                for (int i = 0; i < mDaemons.length; i++) {
1851047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                    if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
1852047454c759b46bbadb87ee3b64bf3e29afda48d6Hisanobu Watanabe                        return;
1853899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                    }
1854899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey                }
1855899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey            }
1856899223b97c9b0ae56a8211a46600914c0ecfd854Jeff Sharkey        }
185785a7ce02c8b53e1efaa5a3a32f0a5ec7b549f758Chia-chi Yeh    }
1858ff3bdca31f4cf2bd607519b276dd175763aa1784Chia-chi Yeh}
1859