title: How to programmatically connect with vpn server without using "Secret key"?
body: <p>I want to connect with VPN server but I don't want to use secret key. Currently the code snippet i found to programmatically create vpn connection is as follows:</p>
  
  <p>MyVpnClient:</p>
  
  <pre><code>package com.example.android.toyvpn;
  import android.app.Activity;
  import android.content.Intent;
  import android.content.SharedPreferences;
  import android.net.VpnService;
  import android.os.Bundle;
  import android.widget.TextView;
  public class ToyVpnClient extends Activity {
      public interface Prefs {
          String NAME = "connection";
          String SERVER_ADDRESS = "server.address";
          String SERVER_PORT = "server.port";
          String SHARED_SECRET = "shared.secret";
      }
      @Override
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.form);
          final TextView serverAddress = (TextView) findViewById(R.id.address);
          final TextView serverPort = (TextView) findViewById(R.id.port);
          final TextView sharedSecret = (TextView) findViewById(R.id.secret);
          final SharedPreferences prefs = getSharedPreferences(Prefs.NAME, MODE_PRIVATE);
          serverAddress.setText(prefs.getString(Prefs.SERVER_ADDRESS, ""));
          serverPort.setText(prefs.getString(Prefs.SERVER_PORT, ""));
          sharedSecret.setText(prefs.getString(Prefs.SHARED_SECRET, ""));
          findViewById(R.id.connect).setOnClickListener(v -&gt; {
              prefs.edit()
                      .putString(Prefs.SERVER_ADDRESS, serverAddress.getText().toString())
                      .putString(Prefs.SERVER_PORT, serverPort.getText().toString())
                      .putString(Prefs.SHARED_SECRET, sharedSecret.getText().toString())
                      .commit();
              Intent intent = VpnService.prepare(ToyVpnClient.this);
              if (intent != null) {
                  startActivityForResult(intent, 0);
              } else {
                  onActivityResult(0, RESULT_OK, null);
              }
          });
          findViewById(R.id.disconnect).setOnClickListener(v -&gt; {
              startService(getServiceIntent().setAction(ToyVpnService.ACTION_DISCONNECT));
          });
      }
      @Override
      protected void onActivityResult(int request, int result, Intent data) {
          if (result == RESULT_OK) {
              startService(getServiceIntent().setAction(ToyVpnService.ACTION_CONNECT));
          }
      }
      private Intent getServiceIntent() {
          return new Intent(this, ToyVpnService.class);
      }
  }
  </code></pre>
  
  <p>MyVpnConnection:</p>
  
  <pre><code>package com.example.android.toyvpn;
  import static java.nio.charset.StandardCharsets.US_ASCII;
  import android.app.PendingIntent;
  import android.net.VpnService;
  import android.os.ParcelFileDescriptor;
  import android.util.Log;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.net.InetSocketAddress;
  import java.net.SocketAddress;
  import java.net.SocketException;
  import java.nio.ByteBuffer;
  import java.nio.channels.DatagramChannel;
  import java.util.concurrent.TimeUnit;
  public class ToyVpnConnection implements Runnable {
      /**
       * Callback interface to let the {@link ToyVpnService} know about new connections
       * and update the foreground notification with connection status.
       */
      public interface OnEstablishListener {
          void onEstablish(ParcelFileDescriptor tunInterface);
      }
      /** Maximum packet size is constrained by the MTU, which is given as a signed short. */
      private static final int MAX_PACKET_SIZE = Short.MAX_VALUE;
      /** Time to wait in between losing the connection and retrying. */
      private static final long RECONNECT_WAIT_MS = TimeUnit.SECONDS.toMillis(3);
      /** Time between keepalives if there is no traffic at the moment.
       *
       * TODO: don't do this; it's much better to let the connection die and then reconnect when
       *       necessary instead of keeping the network hardware up for hours on end in between.
       **/
      private static final long KEEPALIVE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(15);
      /** Time to wait without receiving any response before assuming the server is gone. */
      private static final long RECEIVE_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(20);
      /**
       * Time between polling the VPN interface for new traffic, since it's non-blocking.
       *
       * TODO: really don't do this; a blocking read on another thread is much cleaner.
       */
      private static final long IDLE_INTERVAL_MS = TimeUnit.MILLISECONDS.toMillis(100);
      /**
       * Number of periods of length {@IDLE_INTERVAL_MS} to wait before declaring the handshake a
       * complete and abject failure.
       *
       * TODO: use a higher-level protocol; hand-rolling is a fun but pointless exercise.
       */
      private static final int MAX_HANDSHAKE_ATTEMPTS = 50;
      private final VpnService mService;
      private final int mConnectionId;
      private final String mServerName;
      private final int mServerPort;
      private final byte[] mSharedSecret;
      private PendingIntent mConfigureIntent;
      private OnEstablishListener mOnEstablishListener;
      public ToyVpnConnection(final VpnService service, final int connectionId,
              final String serverName, final int serverPort, final byte[] sharedSecret) {
          mService = service;
          mConnectionId = connectionId;
          mServerName = serverName;
          mServerPort= serverPort;
          mSharedSecret = sharedSecret;
      }
      /**
       * Optionally, set an intent to configure the VPN. This is {@code null} by default.
       */
      public void setConfigureIntent(PendingIntent intent) {
          mConfigureIntent = intent;
      }
      public void setOnEstablishListener(OnEstablishListener listener) {
          mOnEstablishListener = listener;
      }
      @Override
      public void run() {
          try {
              Log.i(getTag(), "Starting");
              // If anything needs to be obtained using the network, get it now.
              // This greatly reduces the complexity of seamless handover, which
              // tries to recreate the tunnel without shutting down everything.
              // In this demo, all we need to know is the server address.
              final SocketAddress serverAddress = new InetSocketAddress(mServerName, mServerPort);
              // We try to create the tunnel several times.
              // TODO: The better way is to work with ConnectivityManager, trying only when the
              //       network is available.
              // Here we just use a counter to keep things simple.
              for (int attempt = 0; attempt &lt; 10; ++attempt) {
                  // Reset the counter if we were connected.
                  if (run(serverAddress)) {
                      attempt = 0;
                  }
                  // Sleep for a while. This also checks if we got interrupted.
                  Thread.sleep(3000);
              }
              Log.i(getTag(), "Giving up");
          } catch (IOException | InterruptedException | IllegalArgumentException e) {
              Log.e(getTag(), "Connection failed, exiting", e);
          }
      }
      private boolean run(SocketAddress server)
              throws IOException, InterruptedException, IllegalArgumentException {
          ParcelFileDescriptor iface = null;
          boolean connected = false;
          // Create a DatagramChannel as the VPN tunnel.
          try (DatagramChannel tunnel = DatagramChannel.open()) {
              // Protect the tunnel before connecting to avoid loopback.
              if (!mService.protect(tunnel.socket())) {
                  throw new IllegalStateException("Cannot protect the tunnel");
              }
              // Connect to the server.
              tunnel.connect(server);
              // For simplicity, we use the same thread for both reading and
              // writing. Here we put the tunnel into non-blocking mode.
              tunnel.configureBlocking(false);
              // Authenticate and configure the virtual network interface.
              iface = handshake(tunnel);
              // Now we are connected. Set the flag.
              connected = true;
              // Packets to be sent are queued in this input stream.
              FileInputStream in = new FileInputStream(iface.getFileDescriptor());
              // Packets received need to be written to this output stream.
              FileOutputStream out = new FileOutputStream(iface.getFileDescriptor());
              // Allocate the buffer for a single packet.
              ByteBuffer packet = ByteBuffer.allocate(MAX_PACKET_SIZE);
              // Timeouts:
              //   - when data has not been sent in a while, send empty keepalive messages.
              //   - when data has not been received in a while, assume the connection is broken.
              long lastSendTime = System.currentTimeMillis();
              long lastReceiveTime = System.currentTimeMillis();
              // We keep forwarding packets till something goes wrong.
              while (true) {
                  // Assume that we did not make any progress in this iteration.
                  boolean idle = true;
                  // Read the outgoing packet from the input stream.
                  int length = in.read(packet.array());
                  if (length &gt; 0) {
                      // Write the outgoing packet to the tunnel.
                      packet.limit(length);
                      tunnel.write(packet);
                      packet.clear();
                      // There might be more outgoing packets.
                      idle = false;
                      lastReceiveTime = System.currentTimeMillis();
                  }
                  // Read the incoming packet from the tunnel.
                  length = tunnel.read(packet);
                  if (length &gt; 0) {
                      // Ignore control messages, which start with zero.
                      if (packet.get(0) != 0) {
                          // Write the incoming packet to the output stream.
                          out.write(packet.array(), 0, length);
                      }
                      packet.clear();
                      // There might be more incoming packets.
                      idle = false;
                      lastSendTime = System.currentTimeMillis();
                  }
                  // If we are idle or waiting for the network, sleep for a
                  // fraction of time to avoid busy looping.
                  if (idle) {
                      Thread.sleep(IDLE_INTERVAL_MS);
                      final long timeNow = System.currentTimeMillis();
                      if (lastSendTime + KEEPALIVE_INTERVAL_MS &lt;= timeNow) {
                          // We are receiving for a long time but not sending.
                          // Send empty control messages.
                          packet.put((byte) 0).limit(1);
                          for (int i = 0; i &lt; 3; ++i) {
                              packet.position(0);
                              tunnel.write(packet);
                          }
                          packet.clear();
                          lastSendTime = timeNow;
                      } else if (lastReceiveTime + RECEIVE_TIMEOUT_MS &lt;= timeNow) {
                          // We are sending for a long time but not receiving.
                          throw new IllegalStateException("Timed out");
                      }
                  }
              }
          } catch (SocketException e) {
              Log.e(getTag(), "Cannot use socket", e);
          } finally {
              if (iface != null) {
                  try {
                      iface.close();
                  } catch (IOException e) {
                      Log.e(getTag(), "Unable to close interface", e);
                  }
              }
          }
          return connected;
      }
      private ParcelFileDescriptor handshake(DatagramChannel tunnel)
              throws IOException, InterruptedException {
          // To build a secured tunnel, we should perform mutual authentication
          // and exchange session keys for encryption. To keep things simple in
          // this demo, we just send the shared secret in plaintext and wait
          // for the server to send the parameters.
          // Allocate the buffer for handshaking. We have a hardcoded maximum
          // handshake size of 1024 bytes, which should be enough for demo
          // purposes.
          ByteBuffer packet = ByteBuffer.allocate(1024);
          // Control messages always start with zero.
          packet.put((byte) 0).put(mSharedSecret).flip();
          // Send the secret several times in case of packet loss.
          for (int i = 0; i &lt; 3; ++i) {
              packet.position(0);
              tunnel.write(packet);
          }
          packet.clear();
          // Wait for the parameters within a limited time.
          for (int i = 0; i &lt; MAX_HANDSHAKE_ATTEMPTS; ++i) {
              Thread.sleep(IDLE_INTERVAL_MS);
              // Normally we should not receive random packets. Check that the first
              // byte is 0 as expected.
              int length = tunnel.read(packet);
              if (length &gt; 0 &amp;&amp; packet.get(0) == 0) {
                  return configure(new String(packet.array(), 1, length - 1, US_ASCII).trim());
              }
          }
          throw new IOException("Timed out");
      }
      private ParcelFileDescriptor configure(String parameters) throws IllegalArgumentException {
          // Configure a builder while parsing the parameters.
          VpnService.Builder builder = mService.new Builder();
          for (String parameter : parameters.split(" ")) {
              String[] fields = parameter.split(",");
              try {
                  switch (fields[0].charAt(0)) {
                      case 'm':
                          builder.setMtu(Short.parseShort(fields[1]));
                          break;
                      case 'a':
                          builder.addAddress(fields[1], Integer.parseInt(fields[2]));
                          break;
                      case 'r':
                          builder.addRoute(fields[1], Integer.parseInt(fields[2]));
                          break;
                      case 'd':
                          builder.addDnsServer(fields[1]);
                          break;
                      case 's':
                          builder.addSearchDomain(fields[1]);
                          break;
                  }
              } catch (NumberFormatException e) {
                  throw new IllegalArgumentException("Bad parameter: " + parameter);
              }
          }
          // Create a new interface using the builder and save the parameters.
          final ParcelFileDescriptor vpnInterface;
          synchronized (mService) {
              vpnInterface = builder
                      .setSession(mServerName)
                      .setConfigureIntent(mConfigureIntent)
                      .establish();
              if (mOnEstablishListener != null) {
                  mOnEstablishListener.onEstablish(vpnInterface);
              }
          }
          Log.i(getTag(), "New interface: " + vpnInterface + " (" + parameters + ")");
          return vpnInterface;
      }
      private final String getTag() {
          return ToyVpnConnection.class.getSimpleName() + "[" + mConnectionId + "]";
      }
  }
  </code></pre>
  
  <p>MyVpnService:</p>
  
  <pre><code>package com.example.android.toyvpn;
  import android.app.Notification;
  import android.app.PendingIntent;
  import android.app.Service;
  import android.content.Intent;
  import android.content.SharedPreferences;
  import android.net.VpnService;
  import android.os.Handler;
  import android.os.Message;
  import android.os.ParcelFileDescriptor;
  import android.util.Log;
  import android.util.Pair;
  import android.widget.Toast;
  import java.io.IOException;
  import java.util.concurrent.atomic.AtomicInteger;
  import java.util.concurrent.atomic.AtomicReference;
  public class ToyVpnService extends VpnService implements Handler.Callback {
      private static final String TAG = ToyVpnService.class.getSimpleName();
      public static final String ACTION_CONNECT = "com.example.android.toyvpn.START";
      public static final String ACTION_DISCONNECT = "com.example.android.toyvpn.STOP";
      private Handler mHandler;
      private static class Connection extends Pair&lt;Thread, ParcelFileDescriptor&gt; {
          public Connection(Thread thread, ParcelFileDescriptor pfd) {
              super(thread, pfd);
          }
      }
      private final AtomicReference&lt;Thread&gt; mConnectingThread = new AtomicReference&lt;&gt;();
      private final AtomicReference&lt;Connection&gt; mConnection = new AtomicReference&lt;&gt;();
      private AtomicInteger mNextConnectionId = new AtomicInteger(1);
      private PendingIntent mConfigureIntent;
      @Override
      public void onCreate() {
          // The handler is only used to show messages.
          if (mHandler == null) {
              mHandler = new Handler(this);
          }
          // Create the intent to "configure" the connection (just start ToyVpnClient).
          mConfigureIntent = PendingIntent.getActivity(this, 0, new Intent(this, ToyVpnClient.class),
                  PendingIntent.FLAG_UPDATE_CURRENT);
      }
      @Override
      public int onStartCommand(Intent intent, int flags, int startId) {
          if (intent != null &amp;&amp; ACTION_DISCONNECT.equals(intent.getAction())) {
              disconnect();
              return START_NOT_STICKY;
          } else {
              connect();
              return START_STICKY;
          }
      }
      @Override
      public void onDestroy() {
          disconnect();
      }
      @Override
      public boolean handleMessage(Message message) {
          Toast.makeText(this, message.what, Toast.LENGTH_SHORT).show();
          if (message.what != R.string.disconnected) {
              updateForegroundNotification(message.what);
          }
          return true;
      }
      private void connect() {
          // Become a foreground service. Background services can be VPN services too, but they can
          // be killed by background check before getting a chance to receive onRevoke().
          updateForegroundNotification(R.string.connecting);
          mHandler.sendEmptyMessage(R.string.connecting);
          // Extract information from the shared preferences.
          final SharedPreferences prefs = getSharedPreferences(ToyVpnClient.Prefs.NAME, MODE_PRIVATE);
          final String server = prefs.getString(ToyVpnClient.Prefs.SERVER_ADDRESS, "");
          final byte[] secret = prefs.getString(ToyVpnClient.Prefs.SHARED_SECRET, "").getBytes();
          final int port;
          try {
              port = Integer.parseInt(prefs.getString(ToyVpnClient.Prefs.SERVER_PORT, ""));
          } catch (NumberFormatException e) {
              Log.e(TAG, "Bad port: " + prefs.getString(ToyVpnClient.Prefs.SERVER_PORT, null), e);
              return;
          }
          // Kick off a connection.
          startConnection(new ToyVpnConnection(
                  this, mNextConnectionId.getAndIncrement(), server, port, secret));
      }
      private void startConnection(final ToyVpnConnection connection) {
          // Replace any existing connecting thread with the  new one.
          final Thread thread = new Thread(connection, "ToyVpnThread");
          setConnectingThread(thread);
          // Handler to mark as connected once onEstablish is called.
          connection.setConfigureIntent(mConfigureIntent);
          connection.setOnEstablishListener(new ToyVpnConnection.OnEstablishListener() {
              public void onEstablish(ParcelFileDescriptor tunInterface) {
                  mHandler.sendEmptyMessage(R.string.connected);
                  mConnectingThread.compareAndSet(thread, null);
                  setConnection(new Connection(thread, tunInterface));
              }
          });
          thread.start();
      }
      private void setConnectingThread(final Thread thread) {
          final Thread oldThread = mConnectingThread.getAndSet(thread);
          if (oldThread != null) {
              oldThread.interrupt();
          }
      }
      private void setConnection(final Connection connection) {
          final Connection oldConnection = mConnection.getAndSet(connection);
          if (oldConnection != null) {
              try {
                  oldConnection.first.interrupt();
                  oldConnection.second.close();
              } catch (IOException e) {
                  Log.e(TAG, "Closing VPN interface", e);
              }
          }
      }
      private void disconnect() {
          mHandler.sendEmptyMessage(R.string.disconnected);
          setConnectingThread(null);
          setConnection(null);
          stopForeground(true);
      }
      private void updateForegroundNotification(final int message) {
          startForeground(1, new Notification.Builder(this)
                  .setSmallIcon(R.drawable.ic_vpn)
                  .setContentText(getString(message))
                  .setContentIntent(mConfigureIntent)
                  .build());
      }
  }
  </code></pre>
  
  <p>The above code is from the following url:</p>
  
  <pre><code>https://android.googlesource.com/platform/development/+/master/samples/ToyVpn
  </code></pre>
  
  <p>I have tried but I am not getting any workaround without secret key. Please help.</p>