如何使用Java Reflection添加自己的VPN设置(Android SDK 14+)

时间:2013-09-23 08:29:51

标签: java android vpn

我知道android.net.VpnService基本上是构建自定义vpn解决方案的基类,但我想要的只是创建和使用PPTP或L2TP VPN连接(只是内置VPN管理器的新配置文件)。 我认为最简单的方法是使用Java反射来进行com.android.settings.vpn.VpnSettings。以下是其他帖子(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);
    }
}

当我运行此代码时,我得到:java.lang.classnotfoundexception: com.android.settings.vpn2.vpnsettings

知道我做错了什么。我尝试使用API​​ 14,15 .. 18 设备未植根。

如果您有其他建议如何将新配置文件添加到内置VPN管理器,请告诉我们。

2 个答案:

答案 0 :(得分:4)

基本上,您尝试的内容无法正常运行,因为您的应用中无法使用VpnSettings课程。它不是Android框架的私有部分;它是Settings应用程序的一部分,它是一个与您完全独立的应用程序。这就像尝试从Firefox应用程序访问org.mozilla.firefox.App类一样。你不能这样做。如果可以的话,您会发现它仍然不起作用,因为VpnSettings会将其设置保存到您的应用无权访问的位置。

如果您的设备 已根,并且您的应用具有超级用户权限,则您可以自行写入该位置。否则,你就没有机会了。

答案 1 :(得分:1)

所以似乎将我的Android Project Build Target更改为Google API目标而不仅仅是Android目标已经修复了问题(或者至少我不再再获得ClassNotFoundException了。)

尝试自己设置:右键单击您的项目。构建路径 - &gt;配置构建路径...转到左窗格中的Android设置。检查项目的相应Google API目标。

编辑:这解决了我遇到的VpnProfile问题,但没有解决您所拥有的VpnSettings问题(现在我遇到了这个问题)。

编辑2:VpnSettings类位于“设置”应用中。如果您愿意,可以下载“设置”应用的来源:https://android.googlesource.com/platform/packages/apps/Settings.git