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
onStartCommand(Intent intent, int flags, int startId)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 
start(String packageName, Intent intent)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 
broadcastEstablished()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 
stop()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
onDestroy()180     public void onDestroy() {
181         stop();
182         super.onDestroy();
183     }
184 }
185