5

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

Community
  • 1
  • 1
dzirtbry
  • 328
  • 2
  • 14

2 Answers2

15

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.

fries
  • 216
  • 3
  • 4
  • Great job! It's quite unfortunate, however, that Callback is a private class. Would be better off if one could create an instance of Callback and delegate the onTransact call to this instance. As of now we rely on code duplication... – Janus Varmarken Oct 29 '14 at 21:18
  • 1
    Thanks, its save my day – Mokhtarabadi Sep 11 '19 at 23:52
  • No need to duplicate `VpnService`'s logic, see [other-answer](https://stackoverflow.com/a/39591917/8740349), which works as long as you don't **wrongly** (manually) set `VpnService.SERVICE_INTERFACE` as action. – Top-Master Nov 20 '22 at 18:43
5

This happens if we use a custom IBinder defined via AIDL, which I return from onBind().

VpnService implements onBind() too and returns an instance of VpnService.Callback private-class.

Solution:

@Override
public IBinder onBind(Intent intent) {
    String action = intent != null ? intent.getAction() : null;
    if (action != null && action.equals(VpnService.SERVICE_INTERFACE)) {
        return super.onBind(intent);
    }

    return yourBinder;
}

Note that above works as long as you never set VpnService.SERVICE_INTERFACE as action manually.

That action should only be set by Android's internal-logic (automatically, never by our code manually).

Top-Master
  • 7,611
  • 5
  • 39
  • 71
August Dong
  • 51
  • 1
  • 1