3

There are system setting for VPN. I am going to add additional VPN service probably based on the VpnService class. As I see there is a method setConfigureIntent which looks similar to something I need but I do not see any examples of usage.

public VpnService.Builder setConfigureIntent (PendingIntent intent)

Added in API level 14 Set the PendingIntent to an activity for users to configure the VPN connection. If it is not set, the button to configure will not be shown in system-managed dialogs.

VPN settings pages are here:

Wireless and networks, VPN.

Actually I just need to add a button to the system VPN settings clicking on which custom dialog with VPN specific settings is shown.

Иван
  • 434
  • 3
  • 16
  • It's not very clear what you are trying to do. Do you want to replicate "Add VPN" dialog and functionality (independently from Settings app)? Want to launch VPN Settings (or show the "Add VPN" dialog of Settings) from your app? – ozbek Aug 29 '13 at 01:48
  • I would like to add an entry to the VPN system settings page. So that the user can click Settings -> More... -> VPN -> My specific VPN settings. I think that on the second screenshot should appear new entry below VPN or something like that. – Иван Aug 29 '13 at 10:11
  • Are you working with an OEM or building a custom ROM? If not, what you are saying is not possible. – ozbek Aug 29 '13 at 11:58
  • I am working with OEM. As I know in general system settings cannot be changed by user application except the case when system supply hooks like that http://stackoverflow.com/questions/12096555/ I thought there could be something similar here for VPN. – Иван Aug 29 '13 at 12:20
  • 1
    Nope, VPN settings does not provide anything like TTS engine settings. You will need to add your changes directly to **packages/apps/Settings/src/com/android/settings/vpn2/VpnSettings.java** – ozbek Aug 29 '13 at 13:16
  • 1
    @shoerat @Иван vpn2.VpnSettings contains the VpnProfile class from which you can create instance with reflection and later on add it to the settings with `addPreferenceFor(VpnProfile vpnProf)` which is from `com.android.settings.vpn.VpnSettings` class to add to container. Another way is to add the VpnProfile to the `mVpnProfileList` field, using `add()` list method via reflection. This is really dirty :) – Nikola Despotoski Aug 31 '13 at 01:48
  • @Иван Have you found a way how to do that? – KennyPowers Sep 23 '13 at 08:03

2 Answers2

3

Here is starting point to what @shoe rat proposed, using Java Reflection:

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);
    }
}

I didn't have time to test it, but surely it will give you a starting point. I'm not sure root access is needed for accessing this hidden API. Please bare in mind that this might throw some SecurityExceptions or IllegalAccessException.

Nikola Despotoski
  • 49,966
  • 15
  • 119
  • 148
  • Hi there, thanks for answer. I'm not sure if it works for topic creator, but I have some problems with this code, i always get `java.lang.classnotfoundexception: com.android.settings.vpn2.vpnsettings`, API's version: 14+. Do you have any ideas why? Thanks a lot. – KennyPowers Sep 23 '13 at 07:54
  • Same problem as KennyPowers. I've tried loading VpnSettings by using Class.forName and ClassLoader.loadClass. Same result. – groomsy Nov 11 '13 at 16:55
  • Can you try looping all classes inside package "com.android.settings" and "com.android.settings.vpn2"? Just see where is the `VpnSettings` class located. – Nikola Despotoski Nov 27 '13 at 04:24
0

If you can not find class,you may try this.

Context vpnset =createPackageContext("com.android.settings",Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
Class<?> vpnSettings = Class.forName("com.android.settings.vpn2.VpnSettings",true,vpnset.getClassLoader());
Raviteja
  • 3,399
  • 23
  • 42
  • 69