dzirtbry dzirtbry - 2 months ago 18
Android Question

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

Answer

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.