我今天有个业务需求就是对商用设备进行流量限制,防止流量无故消耗,因此,要对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:name=".service.NetGuardService " android:permission="android.permission.BIND_VPN_SERVICE"> <intent-filter> <action android:name="android.net.VpnService" /> </intent-filter> <meta-data android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON" android:value="true" /> </service>
这里要注意的是,权限android:permission="android.permission.BIND_VPN_SERVICE"必须在<service>下面,写在外面是没用的。
到这里就基本实现了,我这里用的IP地址都是无效的,所以,addDisallowedApplication真正的含义在于排除,也就是addDisallowedApplication加进去的都是可以正常使用网络的。
经过测试,问题来,就是当手机重启之后,发现服务一直被杀掉。别跟我说什么保活,在这里是没用的。因为根本原因不在于service的生命周期上。经过反复测试,发现一个现象就是手机开机的时候,加载网络驱动是需要时间的,网络连接也是需要时间的。如果在网络没有连接的时候,去调用vpnStart() ,它就会被系统杀死。没办法,只好先做一个网络连接判断,如果没有网络就不去调用,当监听到网络正常时,再去启动它。到此,问题就被修复了。
-----------------------------------------------------
转载请注明来源此处
原地址:#
发表