FileDocCategorySizeDatePackage
Vpn.javaAPI DocAndroid 5.1 API53750Thu Mar 12 22:22:42 GMT 2015com.android.server.connectivity

Vpn

public class Vpn extends Object
hide

Fields Summary
private static final String
NETWORKTYPE
private static final String
TAG
private static final boolean
LOGD
private android.content.Context
mContext
private android.net.NetworkInfo
mNetworkInfo
private String
mPackage
private int
mOwnerUID
private String
mInterface
private Connection
mConnection
private LegacyVpnRunner
mLegacyVpnRunner
private android.app.PendingIntent
mStatusIntent
private volatile boolean
mEnableTeardown
private final android.net.IConnectivityManager
mConnService
private final android.os.INetworkManagementService
mNetd
private com.android.internal.net.VpnConfig
mConfig
private android.net.NetworkAgent
mNetworkAgent
private final android.os.Looper
mLooper
private final android.net.NetworkCapabilities
mNetworkCapabilities
private List
mVpnUsers
private android.content.BroadcastReceiver
mUserIntentReceiver
private final int
mUserHandle
private android.net.INetworkManagementEventObserver
mObserver
Constructors Summary
public Vpn(android.os.Looper looper, android.content.Context context, android.os.INetworkManagementService netService, android.net.IConnectivityManager connService, int userHandle)


          
                
        mContext = context;
        mNetd = netService;
        mConnService = connService;
        mUserHandle = userHandle;
        mLooper = looper;

        mPackage = VpnConfig.LEGACY_VPN;
        mOwnerUID = getAppUid(mPackage, mUserHandle);

        try {
            netService.registerObserver(mObserver);
        } catch (RemoteException e) {
            Log.wtf(TAG, "Problem registering observer", e);
        }
        if (userHandle == UserHandle.USER_OWNER) {
            // Owner's VPN also needs to handle restricted users
            mUserIntentReceiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    final String action = intent.getAction();
                    final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
                            UserHandle.USER_NULL);
                    if (userHandle == UserHandle.USER_NULL) return;

                    if (Intent.ACTION_USER_ADDED.equals(action)) {
                        onUserAdded(userHandle);
                    } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
                        onUserRemoved(userHandle);
                    }
                }
            };

            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction(Intent.ACTION_USER_ADDED);
            intentFilter.addAction(Intent.ACTION_USER_REMOVED);
            mContext.registerReceiverAsUser(
                    mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
        }

        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
        // TODO: Copy metered attribute and bandwidths from physical transport, b/16207332
        mNetworkCapabilities = new NetworkCapabilities();
        mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
        mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
    
Methods Summary
public synchronized booleanaddAddress(java.lang.String address, int prefixLength)

        if (!isCallerEstablishedOwnerLocked()) {
            return false;
        }
        boolean success = jniAddAddress(mInterface, address, prefixLength);
        mNetworkAgent.sendLinkProperties(makeLinkProperties());
        return success;
    
private voidaddVpnUserLocked(int userHandle)

        if (mVpnUsers == null) {
            throw new IllegalStateException("VPN is not active");
        }

        if (mConfig.allowedApplications != null) {
            // Add ranges covering all UIDs for allowedApplications.
            int start = -1, stop = -1;
            for (int uid : getAppsUids(mConfig.allowedApplications, userHandle)) {
                if (start == -1) {
                    start = uid;
                } else if (uid != stop + 1) {
                    mVpnUsers.add(new UidRange(start, stop));
                    start = uid;
                }
                stop = uid;
            }
            if (start != -1) mVpnUsers.add(new UidRange(start, stop));
        } else if (mConfig.disallowedApplications != null) {
            // Add all ranges for user skipping UIDs for disallowedApplications.
            final UidRange userRange = UidRange.createForUser(userHandle);
            int start = userRange.start;
            for (int uid : getAppsUids(mConfig.disallowedApplications, userHandle)) {
                if (uid == start) {
                    start++;
                } else {
                    mVpnUsers.add(new UidRange(start, uid - 1));
                    start = uid + 1;
                }
            }
            if (start <= userRange.stop) mVpnUsers.add(new UidRange(start, userRange.stop));
        } else {
            // Add all UIDs for the user.
            mVpnUsers.add(UidRange.createForUser(userHandle));
        }

        prepareStatusIntent();
    
private voidagentConnect()

        LinkProperties lp = makeLinkProperties();

        if (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute()) {
            mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
        } else {
            mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
        }

        mNetworkInfo.setIsAvailable(true);
        mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);

        NetworkMisc networkMisc = new NetworkMisc();
        networkMisc.allowBypass = mConfig.allowBypass;

        long token = Binder.clearCallingIdentity();
        try {
            mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
                    mNetworkInfo, mNetworkCapabilities, lp, 0, networkMisc) {
                            @Override
                            public void unwanted() {
                                // We are user controlled, not driven by NetworkRequest.
                            }
                        };
        } finally {
            Binder.restoreCallingIdentity(token);
        }

        addVpnUserLocked(mUserHandle);
        // If we are owner assign all Restricted Users to this VPN
        if (mUserHandle == UserHandle.USER_OWNER) {
            token = Binder.clearCallingIdentity();
            List<UserInfo> users;
            try {
                users = UserManager.get(mContext).getUsers();
            } finally {
                Binder.restoreCallingIdentity(token);
            }
            for (UserInfo user : users) {
                if (user.isRestricted()) {
                    addVpnUserLocked(user.id);
                }
            }
        }
        mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
    
private voidagentDisconnect(android.net.NetworkInfo networkInfo, android.net.NetworkAgent networkAgent)

        networkInfo.setIsAvailable(false);
        networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
        if (networkAgent != null) {
            networkAgent.sendNetworkInfo(networkInfo);
        }
    
private voidagentDisconnect(android.net.NetworkAgent networkAgent)

        NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
        agentDisconnect(networkInfo, networkAgent);
    
private voidagentDisconnect()

        if (mNetworkInfo.isConnected()) {
            agentDisconnect(mNetworkInfo, mNetworkAgent);
            mNetworkAgent = null;
        }
    
public synchronized booleanappliesToUid(int uid)

        if (!isRunningLocked()) {
            return false;
        }
        for (UidRange uidRange : mVpnUsers) {
            if (uidRange.start <= uid && uid <= uidRange.stop) {
                return true;
            }
        }
        return false;
    
private voidenforceControlPermission()


       
        mContext.enforceCallingPermission(Manifest.permission.CONTROL_VPN, "Unauthorized Caller");
    
public synchronized android.os.ParcelFileDescriptorestablish(com.android.internal.net.VpnConfig config)
Establish a VPN network and return the file descriptor of the VPN interface. This methods returns {@code null} if the application is revoked or not prepared.

param
config The parameters to configure the network.
return
The file descriptor of the VPN interface.

        // Check if the caller is already prepared.
        UserManager mgr = UserManager.get(mContext);
        if (Binder.getCallingUid() != mOwnerUID) {
            return null;
        }
        // Check if the service is properly declared.
        Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
        intent.setClassName(mPackage, config.user);
        long token = Binder.clearCallingIdentity();
        try {
            // Restricted users are not allowed to create VPNs, they are tied to Owner
            UserInfo user = mgr.getUserInfo(mUserHandle);
            if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
                throw new SecurityException("Restricted users cannot establish VPNs");
            }

            ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
                                                                        null, 0, mUserHandle);
            if (info == null) {
                throw new SecurityException("Cannot find " + config.user);
            }
            if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
                throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
            }
        } catch (RemoteException e) {
                throw new SecurityException("Cannot find " + config.user);
        } finally {
            Binder.restoreCallingIdentity(token);
        }

        // Save the old config in case we need to go back.
        VpnConfig oldConfig = mConfig;
        String oldInterface = mInterface;
        Connection oldConnection = mConnection;
        NetworkAgent oldNetworkAgent = mNetworkAgent;
        mNetworkAgent = null;
        List<UidRange> oldUsers = mVpnUsers;

        // Configure the interface. Abort if any of these steps fails.
        ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
        try {
            updateState(DetailedState.CONNECTING, "establish");
            String interfaze = jniGetName(tun.getFd());

            // TEMP use the old jni calls until there is support for netd address setting
            StringBuilder builder = new StringBuilder();
            for (LinkAddress address : config.addresses) {
                builder.append(" " + address);
            }
            if (jniSetAddresses(interfaze, builder.toString()) < 1) {
                throw new IllegalArgumentException("At least one address must be specified");
            }
            Connection connection = new Connection();
            if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
                        new UserHandle(mUserHandle))) {
                throw new IllegalStateException("Cannot bind " + config.user);
            }

            mConnection = connection;
            mInterface = interfaze;

            // Fill more values.
            config.user = mPackage;
            config.interfaze = mInterface;
            config.startTime = SystemClock.elapsedRealtime();
            mConfig = config;

            // Set up forwarding and DNS rules.
            mVpnUsers = new ArrayList<UidRange>();

            agentConnect();

            if (oldConnection != null) {
                mContext.unbindService(oldConnection);
            }
            // Remove the old tun's user forwarding rules
            // The new tun's user rules have already been added so they will take over
            // as rules are deleted. This prevents data leakage as the rules are moved over.
            agentDisconnect(oldNetworkAgent);
            if (oldInterface != null && !oldInterface.equals(interfaze)) {
                jniReset(oldInterface);
            }

            try {
                IoUtils.setBlocking(tun.getFileDescriptor(), config.blocking);
            } catch (IOException e) {
                throw new IllegalStateException(
                        "Cannot set tunnel's fd as blocking=" + config.blocking, e);
            }
        } catch (RuntimeException e) {
            IoUtils.closeQuietly(tun);
            agentDisconnect();
            // restore old state
            mConfig = oldConfig;
            mConnection = oldConnection;
            mVpnUsers = oldUsers;
            mNetworkAgent = oldNetworkAgent;
            mInterface = oldInterface;
            throw e;
        }
        Log.i(TAG, "Established by " + config.user + " on " + mInterface);
        return tun;
    
private static android.net.RouteInfofindIPv4DefaultRoute(android.net.LinkProperties prop)

        for (RouteInfo route : prop.getAllRoutes()) {
            // Currently legacy VPN only works on IPv4.
            if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
                return route;
            }
        }

        throw new IllegalStateException("Unable to find IPv4 default gateway");
    
private intgetAppUid(java.lang.String app, int userHandle)

        if (VpnConfig.LEGACY_VPN.equals(app)) {
            return Process.myUid();
        }
        PackageManager pm = mContext.getPackageManager();
        int result;
        try {
            result = pm.getPackageUid(app, userHandle);
        } catch (NameNotFoundException e) {
            result = -1;
        }
        return result;
    
private java.util.SortedSetgetAppsUids(java.util.List packageNames, int userHandle)

        SortedSet<Integer> uids = new TreeSet<Integer>();
        for (String app : packageNames) {
            int uid = getAppUid(app, userHandle);
            if (uid != -1) uids.add(uid);
        }
        return uids;
    
public com.android.internal.net.VpnConfiggetLegacyVpnConfig()

        if (mLegacyVpnRunner != null) {
            return mConfig;
        } else {
            return null;
        }
    
public synchronized com.android.internal.net.LegacyVpnInfogetLegacyVpnInfo()
Return the information of the current ongoing legacy VPN.

        // Check if the caller is authorized.
        enforceControlPermission();
        if (mLegacyVpnRunner == null) return null;

        final LegacyVpnInfo info = new LegacyVpnInfo();
        info.key = mConfig.user;
        info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
        if (mNetworkInfo.isConnected()) {
            info.intent = mStatusIntent;
        }
        return info;
    
public android.net.NetworkInfogetNetworkInfo()

        return mNetworkInfo;
    
public synchronized android.net.Network[]getUnderlyingNetworks()

        if (!isRunningLocked()) {
            return null;
        }
        return mConfig.underlyingNetworks;
    
public com.android.internal.net.VpnConfiggetVpnConfig()
Return the configuration of the currently running VPN.

        enforceControlPermission();
        return mConfig;
    
public synchronized voidinterfaceStatusChanged(java.lang.String iface, boolean up)

        try {
            mObserver.interfaceStatusChanged(iface, up);
        } catch (RemoteException e) {
            // ignored; target is local
        }
    
private booleanisCallerEstablishedOwnerLocked()

        return isRunningLocked() && Binder.getCallingUid() == mOwnerUID;
    
private booleanisRunningLocked()

        return mNetworkAgent != null && mInterface != null;
    
private booleanisVpnUserPreConsented(java.lang.String packageName)

        AppOpsManager appOps =
                (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);

        // Verify that the caller matches the given package and has permission to activate VPNs.
        return appOps.noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Binder.getCallingUid(),
                packageName) == AppOpsManager.MODE_ALLOWED;
    
private native booleanjniAddAddress(java.lang.String interfaze, java.lang.String address, int prefixLen)

private native intjniCheck(java.lang.String interfaze)

private native intjniCreate(int mtu)

private native booleanjniDelAddress(java.lang.String interfaze, java.lang.String address, int prefixLen)

private native java.lang.StringjniGetName(int tun)

private native voidjniReset(java.lang.String interfaze)

private native intjniSetAddresses(java.lang.String interfaze, java.lang.String addresses)

private android.net.LinkPropertiesmakeLinkProperties()

        boolean allowIPv4 = mConfig.allowIPv4;
        boolean allowIPv6 = mConfig.allowIPv6;

        LinkProperties lp = new LinkProperties();

        lp.setInterfaceName(mInterface);

        if (mConfig.addresses != null) {
            for (LinkAddress address : mConfig.addresses) {
                lp.addLinkAddress(address);
                allowIPv4 |= address.getAddress() instanceof Inet4Address;
                allowIPv6 |= address.getAddress() instanceof Inet6Address;
            }
        }

        if (mConfig.routes != null) {
            for (RouteInfo route : mConfig.routes) {
                lp.addRoute(route);
                InetAddress address = route.getDestination().getAddress();
                allowIPv4 |= address instanceof Inet4Address;
                allowIPv6 |= address instanceof Inet6Address;
            }
        }

        if (mConfig.dnsServers != null) {
            for (String dnsServer : mConfig.dnsServers) {
                InetAddress address = InetAddress.parseNumericAddress(dnsServer);
                lp.addDnsServer(address);
                allowIPv4 |= address instanceof Inet4Address;
                allowIPv6 |= address instanceof Inet6Address;
            }
        }

        if (!allowIPv4) {
            lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
        }
        if (!allowIPv6) {
            lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
        }

        // Concatenate search domains into a string.
        StringBuilder buffer = new StringBuilder();
        if (mConfig.searchDomains != null) {
            for (String domain : mConfig.searchDomains) {
                buffer.append(domain).append(' ");
            }
        }
        lp.setDomains(buffer.toString().trim());

        // TODO: Stop setting the MTU in jniCreate and set it here.

        return lp;
    
private voidonUserAdded(int userHandle)

        // If the user is restricted tie them to the owner's VPN
        synchronized(Vpn.this) {
            UserManager mgr = UserManager.get(mContext);
            UserInfo user = mgr.getUserInfo(userHandle);
            if (user.isRestricted()) {
                try {
                    addVpnUserLocked(userHandle);
                    if (mNetworkAgent != null) {
                        final List<UidRange> ranges = uidRangesForUser(userHandle);
                        mNetworkAgent.addUidRanges(ranges.toArray(new UidRange[ranges.size()]));
                    }
                } catch (Exception e) {
                    Log.wtf(TAG, "Failed to add restricted user to owner", e);
                }
            }
        }
    
private voidonUserRemoved(int userHandle)

        // clean up if restricted
        synchronized(Vpn.this) {
            UserManager mgr = UserManager.get(mContext);
            UserInfo user = mgr.getUserInfo(userHandle);
            if (user.isRestricted()) {
                try {
                    removeVpnUserLocked(userHandle);
                } catch (Exception e) {
                    Log.wtf(TAG, "Failed to remove restricted user to owner", e);
                }
            }
        }
    
public synchronized booleanprepare(java.lang.String oldPackage, java.lang.String newPackage)
Prepare for a VPN application. This method is designed to solve race conditions. It first compares the current prepared package with {@code oldPackage}. If they are the same, the prepared package is revoked and replaced with {@code newPackage}. If {@code oldPackage} is {@code null}, the comparison is omitted. If {@code newPackage} is the same package or {@code null}, the revocation is omitted. This method returns {@code true} if the operation is succeeded. Legacy VPN is handled specially since it is not a real package. It uses {@link VpnConfig#LEGACY_VPN} as its package name, and it can be revoked by itself.

param
oldPackage The package name of the old VPN application.
param
newPackage The package name of the new VPN application.
return
true if the operation is succeeded.

        if (oldPackage != null && getAppUid(oldPackage, mUserHandle) != mOwnerUID) {
            // The package doesn't match. We return false (to obtain user consent) unless the user
            // has already consented to that VPN package.
            if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) {
                prepareInternal(oldPackage);
                return true;
            }
            return false;
        }

        // Return true if we do not need to revoke.
        if (newPackage == null || (!newPackage.equals(VpnConfig.LEGACY_VPN) &&
                getAppUid(newPackage, mUserHandle) == mOwnerUID)) {
            return true;
        }

        // Check if the caller is authorized.
        enforceControlPermission();

        prepareInternal(newPackage);
        return true;
    
private voidprepareInternal(java.lang.String newPackage)
Prepare the VPN for the given package. Does not perform permission checks.

        long token = Binder.clearCallingIdentity();
        try {
            // Reset the interface.
            if (mInterface != null) {
                mStatusIntent = null;
                agentDisconnect();
                jniReset(mInterface);
                mInterface = null;
                mVpnUsers = null;
            }

            // Revoke the connection or stop LegacyVpnRunner.
            if (mConnection != null) {
                try {
                    mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
                            Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
                } catch (Exception e) {
                    // ignore
                }
                mContext.unbindService(mConnection);
                mConnection = null;
            } else if (mLegacyVpnRunner != null) {
                mLegacyVpnRunner.exit();
                mLegacyVpnRunner = null;
            }

            try {
                mNetd.denyProtect(mOwnerUID);
            } catch (Exception e) {
                Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
            }

            Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
            mPackage = newPackage;
            mOwnerUID = getAppUid(newPackage, mUserHandle);
            try {
                mNetd.allowProtect(mOwnerUID);
            } catch (Exception e) {
                Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
            }
            mConfig = null;

            updateState(DetailedState.IDLE, "prepare");
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    
private voidprepareStatusIntent()

        final long token = Binder.clearCallingIdentity();
        try {
            mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    
public synchronized booleanremoveAddress(java.lang.String address, int prefixLength)

        if (!isCallerEstablishedOwnerLocked()) {
            return false;
        }
        boolean success = jniDelAddress(mInterface, address, prefixLength);
        mNetworkAgent.sendLinkProperties(makeLinkProperties());
        return success;
    
private voidremoveVpnUserLocked(int userHandle)

        if (mVpnUsers == null) {
            throw new IllegalStateException("VPN is not active");
        }
        final List<UidRange> ranges = uidRangesForUser(userHandle);
        if (mNetworkAgent != null) {
            mNetworkAgent.removeUidRanges(ranges.toArray(new UidRange[ranges.size()]));
        }
        mVpnUsers.removeAll(ranges);
        mStatusIntent = null;
    
public voidsetEnableTeardown(boolean enableTeardown)
Set if this object is responsible for watching for {@link NetworkInfo} teardown. When {@code false}, teardown is handled externally by someone else.

        mEnableTeardown = enableTeardown;
    
public voidsetPackageAuthorization(boolean authorized)
Set whether the current package has the ability to launch VPNs without user intervention.

        // Check if the caller is authorized.
        enforceControlPermission();

        if (mPackage == null || VpnConfig.LEGACY_VPN.equals(mPackage)) {
            return;
        }

        long token = Binder.clearCallingIdentity();
        try {
            AppOpsManager appOps =
                    (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
            appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, mOwnerUID, mPackage,
                    authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
        } catch (Exception e) {
            Log.wtf(TAG, "Failed to set app ops for package " + mPackage, e);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    
public synchronized booleansetUnderlyingNetworks(android.net.Network[] networks)

        if (!isCallerEstablishedOwnerLocked()) {
            return false;
        }
        if (networks == null) {
            mConfig.underlyingNetworks = null;
        } else {
            mConfig.underlyingNetworks = new Network[networks.length];
            for (int i = 0; i < networks.length; ++i) {
                if (networks[i] == null) {
                    mConfig.underlyingNetworks[i] = null;
                } else {
                    mConfig.underlyingNetworks[i] = new Network(networks[i].netId);
                }
            }
        }
        return true;
    
public voidstartLegacyVpn(com.android.internal.net.VpnProfile profile, android.security.KeyStore keyStore, android.net.LinkProperties egress)
Start legacy VPN, controlling native daemons as needed. Creates a secondary thread to perform connection work, returning quickly. Should only be called to respond to Binder requests as this enforces caller permission. Use {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, LinkProperties)} to skip the permission check only when the caller is trusted (or the call is initiated by the system).

        enforceControlPermission();
        long token = Binder.clearCallingIdentity();
        try {
            startLegacyVpnPrivileged(profile, keyStore, egress);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    
private synchronized voidstartLegacyVpn(com.android.internal.net.VpnConfig config, java.lang.String[] racoon, java.lang.String[] mtpd)

        stopLegacyVpnPrivileged();

        // Prepare for the new request.
        prepareInternal(VpnConfig.LEGACY_VPN);
        updateState(DetailedState.CONNECTING, "startLegacyVpn");

        // Start a new LegacyVpnRunner and we are done!
        mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
        mLegacyVpnRunner.start();
    
public voidstartLegacyVpnPrivileged(com.android.internal.net.VpnProfile profile, android.security.KeyStore keyStore, android.net.LinkProperties egress)
Like {@link #startLegacyVpn(VpnProfile, KeyStore, LinkProperties)}, but does not check permissions under the assumption that the caller is the system. Callers are responsible for checking permissions if needed.

        if (!keyStore.isUnlocked()) {
            throw new IllegalStateException("KeyStore isn't unlocked");
        }
        UserManager mgr = UserManager.get(mContext);
        UserInfo user = mgr.getUserInfo(mUserHandle);
        if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
            throw new SecurityException("Restricted users cannot establish VPNs");
        }

        final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
        final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
        final String iface = ipv4DefaultRoute.getInterface();

        // Load certificates.
        String privateKey = "";
        String userCert = "";
        String caCert = "";
        String serverCert = "";
        if (!profile.ipsecUserCert.isEmpty()) {
            privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
            userCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
        }
        if (!profile.ipsecCaCert.isEmpty()) {
            byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
            caCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
        }
        if (!profile.ipsecServerCert.isEmpty()) {
            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
            serverCert = (value == null) ? null : new String(value, StandardCharsets.UTF_8);
        }
        if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
            throw new IllegalStateException("Cannot load credentials");
        }

        // Prepare arguments for racoon.
        String[] racoon = null;
        switch (profile.type) {
            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
                racoon = new String[] {
                    iface, profile.server, "udppsk", profile.ipsecIdentifier,
                    profile.ipsecSecret, "1701",
                };
                break;
            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
                racoon = new String[] {
                    iface, profile.server, "udprsa", privateKey, userCert,
                    caCert, serverCert, "1701",
                };
                break;
            case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
                racoon = new String[] {
                    iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
                    profile.ipsecSecret, profile.username, profile.password, "", gateway,
                };
                break;
            case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
                racoon = new String[] {
                    iface, profile.server, "xauthrsa", privateKey, userCert,
                    caCert, serverCert, profile.username, profile.password, "", gateway,
                };
                break;
            case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
                racoon = new String[] {
                    iface, profile.server, "hybridrsa",
                    caCert, serverCert, profile.username, profile.password, "", gateway,
                };
                break;
        }

        // Prepare arguments for mtpd.
        String[] mtpd = null;
        switch (profile.type) {
            case VpnProfile.TYPE_PPTP:
                mtpd = new String[] {
                    iface, "pptp", profile.server, "1723",
                    "name", profile.username, "password", profile.password,
                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
                    (profile.mppe ? "+mppe" : "nomppe"),
                };
                break;
            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
                mtpd = new String[] {
                    iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
                    "name", profile.username, "password", profile.password,
                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
                };
                break;
        }

        VpnConfig config = new VpnConfig();
        config.legacy = true;
        config.user = profile.key;
        config.interfaze = iface;
        config.session = profile.name;

        config.addLegacyRoutes(profile.routes);
        if (!profile.dnsServers.isEmpty()) {
            config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
        }
        if (!profile.searchDomains.isEmpty()) {
            config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
        }
        startLegacyVpn(config, racoon, mtpd);
    
public synchronized voidstopLegacyVpnPrivileged()
Stop legacy VPN. Permissions must be checked by callers.

        if (mLegacyVpnRunner != null) {
            mLegacyVpnRunner.exit();
            mLegacyVpnRunner = null;

            synchronized (LegacyVpnRunner.TAG) {
                // wait for old thread to completely finish before spinning up
                // new instance, otherwise state updates can be out of order.
            }
        }
    
private java.util.ListuidRangesForUser(int userHandle)

        final UidRange userRange = UidRange.createForUser(userHandle);
        final List<UidRange> ranges = new ArrayList<UidRange>();
        for (UidRange range : mVpnUsers) {
            if (range.start >= userRange.start && range.stop <= userRange.stop) {
                ranges.add(range);
            }
        }
        return ranges;
    
private voidupdateState(android.net.NetworkInfo.DetailedState detailedState, java.lang.String reason)
Update current state, dispaching event to listeners.

        if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
        mNetworkInfo.setDetailedState(detailedState, reason, null);
        if (mNetworkAgent != null) {
            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
        }