Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add own VPN settings using Java Reflecion (Android SDK 14+)

Tags:

java

android

vpn

I know that android.net.VpnService is basically a base class to build custom vpn solutions, but all I want is to create and use PPTP or L2TP VPN connection (just a new profile for built-in VPN manager). I think that the easiest way to do that is to use Java reflection for com.android.settings.vpn.VpnSettings. Here's the code snippet for that from the other post (How to add own VPN settings to system VPN settings page?)

package com.nikola.despotoski.whatever;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class VpnSetter {

    private static Map<String , Class<?>> getMappedFields(){
        Map<String , Class<?>> fieldsAndTypes = new HashMap<String, Class<?>>();
        fieldsAndTypes.put("name", String.class);        // 0
        fieldsAndTypes.put("type" , int.class);   // 1
        fieldsAndTypes.put("server", String.class);        // 2
        fieldsAndTypes.put("username", String.class);
        fieldsAndTypes.put("password", String.class);
        fieldsAndTypes.put("dnsServers", String.class);
        fieldsAndTypes.put("searchDomains", String.class);
        fieldsAndTypes.put("routes", String.class);
        fieldsAndTypes.put("mppe", boolean.class);
        fieldsAndTypes.put("l2tpSecret", String.class);
        fieldsAndTypes.put("ipsecIdentifier", String.class);
        fieldsAndTypes.put("ipsecSecret", String.class);
        fieldsAndTypes.put("ipsecUserCert", String.class);
        fieldsAndTypes.put("ipsecCaCert", String.class);
        fieldsAndTypes.put("saveLogin", boolean.class);
        return fieldsAndTypes;
    }
    public static final Set<String> VPN_PROFILE_KEYS = getMappedFields().keySet(); // contains keys for quicker generation of key-value map for each 

    public static void addVpnProfile(String vpnProfileKey, Map<String, Object> values) throws ClassNotFoundException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException{
        Class<?> vpnSettings = Class.forName("com.android.settings.vpn2.VpnSettings");
        Class<?>[] privateVpnSettingsClasses = vpnSettings.getDeclaredClasses();
        Class<?> vpnPreference = null;
        Class<?> vpnProfileClass = Class.forName("com.android.settings.vpn2.VpnProfile");
        for(Class<?> priv :privateVpnSettingsClasses ){
            if(priv.getName().equals("VpnPreference")){
                vpnPreference = priv;
                break;
            }
        }
        Field vpnProfileFromVpnPreferenceField = vpnPreference.getDeclaredField("mProfile");
        vpnProfileFromVpnPreferenceField.setAccessible(true);
        Object vpnProfile = vpnProfileFromVpnPreferenceField.get(vpnProfileClass);
        Constructor<?> constructor = vpnProfileFromVpnPreferenceField.getClass().getConstructors()[0];
        constructor.setAccessible(true);
        vpnProfile = constructor.newInstance(vpnProfileKey);//creating new instance of VpnProfile class
        Map<String, Class<?>> vpnProfileMap = getMappedFields();
        Iterator<String> profileKeysIterator = vpnProfileMap.keySet().iterator();
        while(profileKeysIterator.hasNext()){
            String key = profileKeysIterator.next();
            Field field = vpnProfile.getClass().getField(key);
            field.setAccessible(true);
            if(vpnProfileMap.get(key).equals(String.class) && values.get(key)!=null){
                String s = new String();
                field.set(s, "value");//change this
            }else if(vpnProfileMap.get(key).equals(boolean.class) && values.get(key)!=null){
                int i = 0;
                field.setInt(i, 1111111);// change this
            }else if(values.get(key)!=null){
                boolean  b = false;
                field.setBoolean(b, true);// change this
            }

        }
        vpnSettings = Class.forName("com.android.settings.vpn.VpnSettings"); //time to add it to settings
        Method addProfileMethod = vpnSettings.getDeclaredMethod("addProfile", vpnProfile.getClass()); 
        addProfileMethod.setAccessible(true);
        addProfileMethod.invoke(vpnSettings, vpnProfile);
    }
}

When I run this code I get: java.lang.classnotfoundexception: com.android.settings.vpn2.vpnsettings

Just what to know what I'm doing wrong. I tried with API 14, 15 .. 18 Device is not rooted.

If you have another suggestion how to add a new profile to built-in VPN manager please let me know.

like image 843
KennyPowers Avatar asked Sep 23 '13 08:09

KennyPowers


People also ask

How do I get VPN permission on Android?

On your device, you should navigate to Settings and select Network; 2. Then navigate to the VPN category and tap the options icon next to the installed VPN apps on your device; 3.

Can I use JDK 17 with Android studio?

As per the docs, JDK 17 isn't supported yet in Android Studio.


2 Answers

Basically, what you're trying isn't going to work because the VpnSettings class is not available within your app. It's not a private part of the Android framework; it's part of the Settings app, which is a completely separate app from yours. It's like trying to access the org.mozilla.firefox.App class from the Firefox app. You can't do it. And if you could, you'd find that it still wouldn't work, because VpnSettings saves its settings to a location that your app doesn't have permission to access.

If your device was rooted, and your app had root access, you could potentially write to that location yourself. Otherwise, you have no chance at this.

like image 145
j__m Avatar answered Oct 02 '22 08:10

j__m


So it seems that changing my Android Project Build Target to the Google APIs target instead of just the Android target has fixed the issue (or at least I no longer get the ClassNotFoundException any longer).

Try setting this yourself: Right click on your project. Build Path -> Configure Build Path… Go to the Android settings in the left pane. Check the corresponding Google APIs target for your project.

EDIT: This fixed the VpnProfile issue I had, but not the VpnSettings that you're having (now I'm having that issue).

EDIT 2: VpnSettings class is located in the Settings app. You can download the source for the Settings app if you'd like: https://android.googlesource.com/platform/packages/apps/Settings.git

like image 27
groomsy Avatar answered Oct 02 '22 07:10

groomsy