1 /* 2 * Copyright (C) 2009, 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.server.vpn; 18 19 import android.app.Service; 20 import android.content.Intent; 21 import android.net.vpn.IVpnService; 22 import android.net.vpn.L2tpIpsecProfile; 23 import android.net.vpn.L2tpIpsecPskProfile; 24 import android.net.vpn.L2tpProfile; 25 import android.net.vpn.PptpProfile; 26 import android.net.vpn.VpnManager; 27 import android.net.vpn.VpnProfile; 28 import android.net.vpn.VpnState; 29 import android.os.Environment; 30 import android.os.IBinder; 31 import android.os.SystemProperties; 32 import android.util.Log; 33 34 import java.io.File; 35 import java.io.FileInputStream; 36 import java.io.FileNotFoundException; 37 import java.io.FileOutputStream; 38 import java.io.IOException; 39 import java.io.ObjectInputStream; 40 import java.io.ObjectOutputStream; 41 42 /** 43 * The service class for managing a VPN connection. It implements the 44 * {@link IVpnService} binder interface. 45 */ 46 public class VpnServiceBinder extends Service { 47 private static final String TAG = VpnServiceBinder.class.getSimpleName(); 48 private static final boolean DBG = true; 49 50 private static final String STATES_FILE_RELATIVE_PATH = "/misc/vpn/.states"; 51 52 // The actual implementation is delegated to the VpnService class. 53 private VpnService<? extends VpnProfile> mService; 54 55 // TODO(oam): Test VPN when EFS is enabled (will do later)... 56 private static String getStateFilePath() { 57 // This call will return the correcu directory whether Encrypted FS is enabled or not 58 // Disabled: /data/misc/vpn/.states Enabled: /data/secure/misc/vpn/.states 59 return Environment.getSecureDataDirectory().getPath() + STATES_FILE_RELATIVE_PATH; 60 } 61 62 private final IBinder mBinder = new IVpnService.Stub() { 63 public boolean connect(VpnProfile p, String username, String password) { 64 return VpnServiceBinder.this.connect(p, username, password); 65 } 66 67 public void disconnect() { 68 VpnServiceBinder.this.disconnect(); 69 } 70 71 public void checkStatus(VpnProfile p) { 72 VpnServiceBinder.this.checkStatus(p); 73 } 74 }; 75 76 @Override 77 public void onCreate() { 78 super.onCreate(); 79 checkSavedStates(); 80 } 81 82 83 @Override 84 public void onStart(Intent intent, int startId) { 85 super.onStart(intent, startId); 86 } 87 88 @Override 89 public IBinder onBind(Intent intent) { 90 return mBinder; 91 } 92 93 void saveStates() throws IOException { 94 if (DBG) Log.d("VpnServiceBinder", " saving states"); 95 ObjectOutputStream oos = 96 new ObjectOutputStream(new FileOutputStream(getStateFilePath())); 97 oos.writeObject(mService); 98 oos.close(); 99 } 100 101 void removeStates() { 102 try { 103 File f = new File(getStateFilePath()); 104 if (f.exists()) f.delete(); 105 } catch (Throwable e) { 106 if (DBG) Log.d("VpnServiceBinder", " remove states: " + e); 107 } 108 } 109 110 private synchronized boolean connect(final VpnProfile p, 111 final String username, final String password) { 112 if (mService != null) return false; 113 final VpnService s = mService = createService(p); 114 115 new Thread(new Runnable() { 116 public void run() { 117 s.onConnect(username, password); 118 } 119 }).start(); 120 return true; 121 } 122 123 private synchronized void disconnect() { 124 if (mService == null) return; 125 final VpnService s = mService; 126 127 new Thread(new Runnable() { 128 public void run() { 129 s.onDisconnect(); 130 } 131 }).start(); 132 } 133 134 private synchronized void checkStatus(VpnProfile p) { 135 if ((mService == null) 136 || (!p.getName().equals(mService.mProfile.getName()))) { 137 broadcastConnectivity(p.getName(), VpnState.IDLE); 138 } else { 139 broadcastConnectivity(p.getName(), mService.getState()); 140 } 141 } 142 143 private void checkSavedStates() { 144 try { 145 ObjectInputStream ois = new ObjectInputStream(new FileInputStream( 146 getStateFilePath())); 147 mService = (VpnService<? extends VpnProfile>) ois.readObject(); 148 mService.recover(this); 149 ois.close(); 150 } catch (FileNotFoundException e) { 151 // do nothing 152 } catch (Throwable e) { 153 Log.i("VpnServiceBinder", "recovery error, remove states: " + e); 154 removeStates(); 155 } 156 } 157 158 private VpnService<? extends VpnProfile> createService(VpnProfile p) { 159 switch (p.getType()) { 160 case L2TP: 161 L2tpService l2tp = new L2tpService(); 162 l2tp.setContext(this, (L2tpProfile) p); 163 return l2tp; 164 165 case PPTP: 166 PptpService pptp = new PptpService(); 167 pptp.setContext(this, (PptpProfile) p); 168 return pptp; 169 170 case L2TP_IPSEC_PSK: 171 L2tpIpsecPskService psk = new L2tpIpsecPskService(); 172 psk.setContext(this, (L2tpIpsecPskProfile) p); 173 return psk; 174 175 case L2TP_IPSEC: 176 L2tpIpsecService l2tpIpsec = new L2tpIpsecService(); 177 l2tpIpsec.setContext(this, (L2tpIpsecProfile) p); 178 return l2tpIpsec; 179 180 default: 181 return null; 182 } 183 } 184 185 private void broadcastConnectivity(String name, VpnState s) { 186 new VpnManager(this).broadcastConnectivity(name, s); 187 } 188 } 189