Home | History | Annotate | Download | only in hostside
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.cts.net.hostside;
     18 
     19 import android.content.Intent;
     20 import android.net.Network;
     21 import android.net.ProxyInfo;
     22 import android.net.VpnService;
     23 import android.os.ParcelFileDescriptor;
     24 import android.content.pm.PackageManager.NameNotFoundException;
     25 import android.text.TextUtils;
     26 import android.util.Log;
     27 
     28 import java.io.IOException;
     29 import java.net.InetAddress;
     30 import java.net.UnknownHostException;
     31 import java.util.ArrayList;
     32 
     33 public class MyVpnService extends VpnService {
     34 
     35     private static String TAG = "MyVpnService";
     36     private static int MTU = 1799;
     37 
     38     public static final String ACTION_ESTABLISHED = "com.android.cts.net.hostside.ESTABNLISHED";
     39     public static final String EXTRA_ALWAYS_ON = "is-always-on";
     40     public static final String EXTRA_LOCKDOWN_ENABLED = "is-lockdown-enabled";
     41 
     42     private ParcelFileDescriptor mFd = null;
     43     private PacketReflector mPacketReflector = null;
     44 
     45     @Override
     46     public int onStartCommand(Intent intent, int flags, int startId) {
     47         String packageName = getPackageName();
     48         String cmd = intent.getStringExtra(packageName + ".cmd");
     49         if ("disconnect".equals(cmd)) {
     50             stop();
     51         } else if ("connect".equals(cmd)) {
     52             start(packageName, intent);
     53         }
     54 
     55         return START_NOT_STICKY;
     56     }
     57 
     58     private void start(String packageName, Intent intent) {
     59         Builder builder = new Builder();
     60 
     61         String addresses = intent.getStringExtra(packageName + ".addresses");
     62         if (addresses != null) {
     63             String[] addressArray = addresses.split(",");
     64             for (int i = 0; i < addressArray.length; i++) {
     65                 String[] prefixAndMask = addressArray[i].split("/");
     66                 try {
     67                     InetAddress address = InetAddress.getByName(prefixAndMask[0]);
     68                     int prefixLength = Integer.parseInt(prefixAndMask[1]);
     69                     builder.addAddress(address, prefixLength);
     70                 } catch (UnknownHostException|NumberFormatException|
     71                          ArrayIndexOutOfBoundsException e) {
     72                     continue;
     73                 }
     74             }
     75         }
     76 
     77         String routes = intent.getStringExtra(packageName + ".routes");
     78         if (routes != null) {
     79             String[] routeArray = routes.split(",");
     80             for (int i = 0; i < routeArray.length; i++) {
     81                 String[] prefixAndMask = routeArray[i].split("/");
     82                 try {
     83                     InetAddress address = InetAddress.getByName(prefixAndMask[0]);
     84                     int prefixLength = Integer.parseInt(prefixAndMask[1]);
     85                     builder.addRoute(address, prefixLength);
     86                 } catch (UnknownHostException|NumberFormatException|
     87                          ArrayIndexOutOfBoundsException e) {
     88                     continue;
     89                 }
     90             }
     91         }
     92 
     93         String allowed = intent.getStringExtra(packageName + ".allowedapplications");
     94         if (allowed != null) {
     95             String[] packageArray = allowed.split(",");
     96             for (int i = 0; i < packageArray.length; i++) {
     97                 String allowedPackage = packageArray[i];
     98                 if (!TextUtils.isEmpty(allowedPackage)) {
     99                     try {
    100                         builder.addAllowedApplication(allowedPackage);
    101                     } catch(NameNotFoundException e) {
    102                         continue;
    103                     }
    104                 }
    105             }
    106         }
    107 
    108         String disallowed = intent.getStringExtra(packageName + ".disallowedapplications");
    109         if (disallowed != null) {
    110             String[] packageArray = disallowed.split(",");
    111             for (int i = 0; i < packageArray.length; i++) {
    112                 String disallowedPackage = packageArray[i];
    113                 if (!TextUtils.isEmpty(disallowedPackage)) {
    114                     try {
    115                         builder.addDisallowedApplication(disallowedPackage);
    116                     } catch(NameNotFoundException e) {
    117                         continue;
    118                     }
    119                 }
    120             }
    121         }
    122 
    123         ArrayList<Network> underlyingNetworks =
    124                 intent.getParcelableArrayListExtra(packageName + ".underlyingNetworks");
    125         if (underlyingNetworks == null) {
    126             // VPN tracks default network
    127             builder.setUnderlyingNetworks(null);
    128         } else {
    129             builder.setUnderlyingNetworks(underlyingNetworks.toArray(new Network[0]));
    130         }
    131 
    132         boolean isAlwaysMetered = intent.getBooleanExtra(packageName + ".isAlwaysMetered", false);
    133         builder.setMetered(isAlwaysMetered);
    134 
    135         ProxyInfo vpnProxy = intent.getParcelableExtra(packageName + ".httpProxy");
    136         builder.setHttpProxy(vpnProxy);
    137         builder.setMtu(MTU);
    138         builder.setBlocking(true);
    139         builder.setSession("MyVpnService");
    140 
    141         Log.i(TAG, "Establishing VPN,"
    142                 + " addresses=" + addresses
    143                 + " routes=" + routes
    144                 + " allowedApplications=" + allowed
    145                 + " disallowedApplications=" + disallowed);
    146 
    147         mFd = builder.establish();
    148         Log.i(TAG, "Established, fd=" + (mFd == null ? "null" : mFd.getFd()));
    149 
    150         broadcastEstablished();
    151 
    152         mPacketReflector = new PacketReflector(mFd.getFileDescriptor(), MTU);
    153         mPacketReflector.start();
    154     }
    155 
    156     private void broadcastEstablished() {
    157         final Intent bcIntent = new Intent(ACTION_ESTABLISHED);
    158         bcIntent.putExtra(EXTRA_ALWAYS_ON, isAlwaysOn());
    159         bcIntent.putExtra(EXTRA_LOCKDOWN_ENABLED, isLockdownEnabled());
    160         sendBroadcast(bcIntent);
    161     }
    162 
    163     private void stop() {
    164         if (mPacketReflector != null) {
    165             mPacketReflector.interrupt();
    166             mPacketReflector = null;
    167         }
    168         try {
    169             if (mFd != null) {
    170                 Log.i(TAG, "Closing filedescriptor");
    171                 mFd.close();
    172             }
    173         } catch(IOException e) {
    174         } finally {
    175             mFd = null;
    176         }
    177     }
    178 
    179     @Override
    180     public void onDestroy() {
    181         stop();
    182         super.onDestroy();
    183     }
    184 }
    185