Search code examples
androidvpn

How to add additional handlings to Vpn disconnection in Android?


I have a custom implementation of VpnService, which needs to perform some additional cleanup on disconnection. Everything works fine when I am stoping VpnService from my application using service bindings, but I need to perform that cleanup when client is disconnecting from the Vpn using system dialog.

So, how can I catch the disconnection and add some handlings to that?

Get VPN Connection status on Android - this could be the solution, but it's not working on android 4+.

From logs point of view there are only two entries:

03-20 03:27:09.478: INFO/Vpn(504): Switched from org.my.package to [Legacy VPN] 03-20 03:27:09.478: DEBUG/Vpn(504): setting state=IDLE, reason=prepare


Solution

  • I just ran into the same issue. VpnService.onRevoke() is not called.

    It turns out this happens because I use a custom IBinder defined via AIDL wich I return from onBind(). VpnService implements onBind() too and returns an instance of VpnService.Callback. Which is implemented this way:

    private class Callback extends Binder {
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
            if (code == IBinder.LAST_CALL_TRANSACTION) {
                onRevoke();
                return true;
            }
            return false;
        }
    }
    

    VpnService.Callback does not use AIDL and just checks if the function code IBinder.LAST_CALL_TRANSACTION was sent. If so it executes onRevoke().

    I integrated this code fragment in my custom IBinder implementation and now I receive the onRevoke() message. See the following example:

    private final IBinder mBinder = new ServiceBinder();    
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    public final class ServiceBinder extends ICustomVpnService.Stub
    {
        ... implement methods defined in ICustomVpnService.Stub ....
    
        /**
         * Intercept remote method calls and check for "onRevoke" code which
         * is represented by IBinder.LAST_CALL_TRANSACTION. If onRevoke message
         * was received, call onRevoke() otherwise delegate to super implementation.
         */
        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException
        {
            // see Implementation of android.net.VpnService.Callback.onTransact()
            if ( code == IBinder.LAST_CALL_TRANSACTION )
            {
                onRevoke();
                return true;
            }
            return super.onTransact( code, data, reply, flags );
        }
    
        private void onRevoke()
        {
            // shutdown VpnService, e.g. call VpnService.stopSelf()
        }
    }
    

    How did I figure it out? I searched the android source code for where onRevoke() is actually invoked. For that I find grepcode (android) pretty helpful. I often read the android source to understand how things work.