您的当前位置:首页正文

网络防火墙---使用VpnService遇到的坑

来源:华佗小知识

我今天有个业务需求就是对商用设备进行流量限制,防止流量无故消耗,因此,要对APP的使用网络进行限制,并不需要root,嗯,VpnService就是解决这件事情的。先把代码贴出来:


    private static final String TAG = "NetGuard.Service";

    private static final String EXTRA_COMMAND = "Command";

    private ParcelFileDescriptor vpn = null;

    public static final int START = 1;

    public static final int RELOAD = 2;

    public static final int STOP = 3;

    @Override
    public void onCreate() {
        // Listen for connectivity updates
        IntentFilter ifConnectivity = new IntentFilter();
        ifConnectivity.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        registerReceiver(connectivityChangedReceiver, ifConnectivity);
        super.onCreate();
    }

    private BroadcastReceiver connectivityChangedReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.i(TAG, "Received " + intent);
            if (intent.hasExtra(ConnectivityManager.EXTRA_NETWORK_TYPE))
                reload(BlackHoleService.this);
        }
    };

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // Get command
        int cmd = intent.getIntExtra(EXTRA_COMMAND, RELOAD);
        Log.e(TAG, "执行:" + cmd);
        // Process command
        switch (cmd) {
            case START:
                if (NetworkUtils.isNetworkAvailable(this) && vpn == null) {
                    vpnStart();
                }
                break;
            case RELOAD:
                ParcelFileDescriptor prev = vpn;
                vpnStart();
                if (prev != null)
                    vpnStop(prev);
                break;
            case STOP:
                if (vpn != null) {
                    vpnStop(vpn);
                    vpn = null;
                }
                stopSelf();
                break;
        }
        return START_STICKY;
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void vpnStart() {
        Log.e(TAG, "Starting");
        final Builder builder = new Builder();
        builder.setSession(getString(R.string.app_name));
        builder.addAddress("10.1.10.1", 32);
        builder.addAddress("fd00:1:fd00:1:fd00:1:fd00:1", 128);
        builder.addRoute("0.0.0.0", 0);
        builder.addRoute("0:0:0:0:0:0:0:0", 0);
        try {
            builder.addDisallowedApplication(MainActivity.ALLOW_PACKAGE_NAME);
            builder.addDisallowedApplication("com.google.android.gms");
            builder.addDisallowedApplication(getPackageName());
            vpn = builder.establish();
            Log.e(TAG, "启动完成");
        } catch (Exception e) {
            Log.e(TAG, "大爷的,是不是这里有问题?");
            Log.e(TAG, e.toString());
        }
    }

    private void vpnStop(ParcelFileDescriptor prev) {
        if (prev != null) {
            try {
                prev.close();
            } catch (IOException ex) {
                Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
            }
        }
    }

    public static void start(Context context) {
        Intent intent = new Intent(context, BlackHoleService.class);
        intent.putExtra(EXTRA_COMMAND, START);
        context.startService(intent);
    }

    public static void stop(Context context) {
        Intent intent = new Intent(context, BlackHoleService.class);
        intent.putExtra(EXTRA_COMMAND, STOP);
        context.startService(intent);
    }

    public static void reload(Context context) {
        if (BlockUtils.isLock()) {
            Intent intent = new Intent(context, BlackHoleService.class);
            intent.putExtra(EXTRA_COMMAND, RELOAD);
            context.startService(intent);
        }
    }

    public static Intent isVpnServicePrepared(Context context) {
        Intent prepare = null;
        try {
            return VpnService.prepare(context.getApplicationContext());
        } catch (Exception ex) {
            Log.e(TAG, ex.toString());
        }
        return prepare;
    }

    @Override
    public void onDestroy() {
        Log.e(TAG, "VPNService Destroy");
        unregisterReceiver(connectivityChangedReceiver);
        super.onDestroy();
    }

在AndroidManifest.xml中注册:

<service
             "
            android:permission="android.permission.BIND_VPN_SERVICE">
            <intent-filter>
                <action  />
            </intent-filter>
            <meta-data
                
                android:value="true" />
        </service>

这里要注意的是,权限android:permission="android.permission.BIND_VPN_SERVICE"必须在<service>下面,写在外面是没用的。
到这里就基本实现了,我这里用的IP地址都是无效的,所以,addDisallowedApplication真正的含义在于排除,也就是addDisallowedApplication加进去的都是可以正常使用网络的。
经过测试,问题来,就是当手机重启之后,发现服务一直被杀掉。别跟我说什么保活,在这里是没用的。因为根本原因不在于service的生命周期上。经过反复测试,发现一个现象就是手机开机的时候,加载网络驱动是需要时间的,网络连接也是需要时间的。如果在网络没有连接的时候,去调用vpnStart() ,它就会被系统杀死。没办法,只好先做一个网络连接判断,如果没有网络就不去调用,当监听到网络正常时,再去启动它。到此,问题就被修复了。