From 08fcccc93dce0b7ecdb16af3b5fd2585b452aaf1 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Tue, 19 Nov 2013 15:44:06 +0100 Subject: Android 4.4 workaround, --- icsopenvpn.iml | 32 +- src/de/blinkt/openvpn/LogWindow.java | 9 + .../blinkt/openvpn/api/ExternalOpenVPNService.java | 498 ++++++++++----------- src/de/blinkt/openvpn/core/OpenVPNThread.java | 3 + .../openvpn/core/OpenVpnManagementThread.java | 11 +- src/de/blinkt/openvpn/core/OpenVpnService.java | 33 ++ 6 files changed, 320 insertions(+), 266 deletions(-) diff --git a/icsopenvpn.iml b/icsopenvpn.iml index 5c38a169..ad327925 100644 --- a/icsopenvpn.iml +++ b/icsopenvpn.iml @@ -27,30 +27,31 @@ - + - - - - - - - - - - - - + + + + + + + + + + + + + - - + + @@ -64,6 +65,7 @@ + diff --git a/src/de/blinkt/openvpn/LogWindow.java b/src/de/blinkt/openvpn/LogWindow.java index 82a2f4b6..843f85cc 100644 --- a/src/de/blinkt/openvpn/LogWindow.java +++ b/src/de/blinkt/openvpn/LogWindow.java @@ -2,6 +2,7 @@ package de.blinkt.openvpn; import android.app.Activity; import android.os.Bundle; +import android.view.MenuItem; /** * Created by arne on 13.10.13. @@ -11,5 +12,13 @@ public class LogWindow extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.log_window); + getActionBar().setDisplayHomeAsUpEnabled(true); + + } + + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return super.onOptionsItemSelected(item); } } diff --git a/src/de/blinkt/openvpn/api/ExternalOpenVPNService.java b/src/de/blinkt/openvpn/api/ExternalOpenVPNService.java index 77374355..34b61325 100644 --- a/src/de/blinkt/openvpn/api/ExternalOpenVPNService.java +++ b/src/de/blinkt/openvpn/api/ExternalOpenVPNService.java @@ -35,272 +35,272 @@ import de.blinkt.openvpn.core.VPNLaunchHelper; public class ExternalOpenVPNService extends Service implements StateListener { - private static final int SEND_TOALL = 0; + private static final int SEND_TOALL = 0; - final RemoteCallbackList mCallbacks = - new RemoteCallbackList(); + final RemoteCallbackList mCallbacks = + new RemoteCallbackList(); - private OpenVpnService mService; - private ExternalAppDatabase mExtAppDb; + private OpenVpnService mService; + private ExternalAppDatabase mExtAppDb; - private ServiceConnection mConnection = new ServiceConnection() { + private ServiceConnection mConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName className, - IBinder service) { - // We've bound to LocalService, cast the IBinder and get LocalService instance - LocalBinder binder = (LocalBinder) service; - mService = binder.getService(); - } + @Override + public void onServiceConnected(ComponentName className, + IBinder service) { + // We've bound to LocalService, cast the IBinder and get LocalService instance + LocalBinder binder = (LocalBinder) service; + mService = binder.getService(); + } - @Override - public void onServiceDisconnected(ComponentName arg0) { - mService =null; - } - - }; - - @Override - public void onCreate() { - super.onCreate(); - VpnStatus.addStateListener(this); - mExtAppDb = new ExternalAppDatabase(this); + @Override + public void onServiceDisconnected(ComponentName arg0) { + mService = null; + } + + }; + + @Override + public void onCreate() { + super.onCreate(); + VpnStatus.addStateListener(this); + mExtAppDb = new ExternalAppDatabase(this); - Intent intent = new Intent(getBaseContext(), OpenVpnService.class); - intent.setAction(OpenVpnService.START_SERVICE); - - bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - mHandler.setService(this); - } - - private final IOpenVPNAPIService.Stub mBinder = new IOpenVPNAPIService.Stub() { + Intent intent = new Intent(getBaseContext(), OpenVpnService.class); + intent.setAction(OpenVpnService.START_SERVICE); + + bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + mHandler.setService(this); + } + + private final IOpenVPNAPIService.Stub mBinder = new IOpenVPNAPIService.Stub() { - private void checkOpenVPNPermission() throws SecurityRemoteException{ - PackageManager pm = getPackageManager(); - - for (String apppackage:mExtAppDb.getExtAppList()) { - ApplicationInfo app; - try { - app = pm.getApplicationInfo(apppackage, 0); - if (Binder.getCallingUid() == app.uid) { - return; - } - } catch (NameNotFoundException e) { - // App not found. Remove it from the list - mExtAppDb.removeApp(apppackage); - e.printStackTrace(); - } + private void checkOpenVPNPermission() throws SecurityRemoteException { + PackageManager pm = getPackageManager(); + + for (String apppackage : mExtAppDb.getExtAppList()) { + ApplicationInfo app; + try { + app = pm.getApplicationInfo(apppackage, 0); + if (Binder.getCallingUid() == app.uid) { + return; + } + } catch (NameNotFoundException e) { + // App not found. Remove it from the list + mExtAppDb.removeApp(apppackage); + e.printStackTrace(); + } - } - throw new SecurityException("Unauthorized OpenVPN API Caller"); - } - - @Override - public List getProfiles() throws RemoteException { - checkOpenVPNPermission(); + } + throw new SecurityException("Unauthorized OpenVPN API Caller"); + } + + @Override + public List getProfiles() throws RemoteException { + checkOpenVPNPermission(); - ProfileManager pm = ProfileManager.getInstance(getBaseContext()); + ProfileManager pm = ProfileManager.getInstance(getBaseContext()); - List profiles = new LinkedList(); + List profiles = new LinkedList(); - for(VpnProfile vp: pm.getProfiles()) - profiles.add(new APIVpnProfile(vp.getUUIDString(),vp.mName,vp.mUserEditable)); + for (VpnProfile vp : pm.getProfiles()) + profiles.add(new APIVpnProfile(vp.getUUIDString(), vp.mName, vp.mUserEditable)); - return profiles; - } + return profiles; + } - @Override - public void startProfile(String profileUUID) throws RemoteException { - checkOpenVPNPermission(); - - Intent shortVPNIntent = new Intent(Intent.ACTION_MAIN); - shortVPNIntent.setClass(getBaseContext(),de.blinkt.openvpn.LaunchVPN.class); - shortVPNIntent.putExtra(de.blinkt.openvpn.LaunchVPN.EXTRA_KEY,profileUUID); - shortVPNIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(shortVPNIntent); - } + @Override + public void startProfile(String profileUUID) throws RemoteException { + checkOpenVPNPermission(); + + Intent shortVPNIntent = new Intent(Intent.ACTION_MAIN); + shortVPNIntent.setClass(getBaseContext(), de.blinkt.openvpn.LaunchVPN.class); + shortVPNIntent.putExtra(de.blinkt.openvpn.LaunchVPN.EXTRA_KEY, profileUUID); + shortVPNIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(shortVPNIntent); + } - public void startVPN(String inlineconfig) throws RemoteException { - checkOpenVPNPermission(); + public void startVPN(String inlineconfig) throws RemoteException { + checkOpenVPNPermission(); - ConfigParser cp = new ConfigParser(); - try { - cp.parseConfig(new StringReader(inlineconfig)); - VpnProfile vp = cp.convertProfile(); - if(vp.checkProfile(getApplicationContext()) != R.string.no_error_found) - throw new RemoteException(getString(vp.checkProfile(getApplicationContext()))); - - - ProfileManager.setTemporaryProfile(vp); - VPNLaunchHelper.startOpenVpn(vp, getBaseContext()); - - - } catch (IOException e) { - throw new RemoteException(e.getMessage()); - } catch (ConfigParseError e) { - throw new RemoteException(e.getMessage()); - } - } - - @Override - public boolean addVPNProfile(String name, String config) throws RemoteException { - checkOpenVPNPermission(); - - ConfigParser cp = new ConfigParser(); - try { - cp.parseConfig(new StringReader(config)); - VpnProfile vp = cp.convertProfile(); - vp.mName = name; - ProfileManager pm = ProfileManager.getInstance(getBaseContext()); - pm.addProfile(vp); - } catch (IOException e) { - e.printStackTrace(); - return false; - } catch (ConfigParseError e) { - e.printStackTrace(); - return false; - } - - return true; - } - - - @Override - public Intent prepare(String packagename) { - if (new ExternalAppDatabase(ExternalOpenVPNService.this).isAllowed(packagename)) - return null; - - Intent intent = new Intent(); - intent.setClass(ExternalOpenVPNService.this, ConfirmDialog.class); - return intent; - } - - @Override - public Intent prepareVPNService() throws RemoteException { - checkOpenVPNPermission(); - - if(VpnService.prepare(ExternalOpenVPNService.this)==null) - return null; - else - return new Intent(getBaseContext(), GrantPermissionsActivity.class); - } - - - @Override - public void registerStatusCallback(IOpenVPNStatusCallback cb) - throws RemoteException { - checkOpenVPNPermission(); - - if (cb != null) { - cb.newStatus(mMostRecentState.vpnUUID, mMostRecentState.state, - mMostRecentState.logmessage, mMostRecentState.level.name()); - mCallbacks.register(cb); - } - - - } - - @Override - public void unregisterStatusCallback(IOpenVPNStatusCallback cb) - throws RemoteException { - checkOpenVPNPermission(); - - if (cb != null) - mCallbacks.unregister(cb); - } - - @Override - public void disconnect() throws RemoteException { - checkOpenVPNPermission(); - if(mService!=null && mService.getManagement()!=null) - mService.getManagement().stopVPN(); - } - }; - - - - private UpdateMessage mMostRecentState; @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - @Override - public void onDestroy() { - super.onDestroy(); - mCallbacks.kill(); - unbindService(mConnection); - VpnStatus.removeStateListener(this); - } - - class UpdateMessage { - public String state; - public String logmessage; - public ConnectionStatus level; - public String vpnUUID; - - public UpdateMessage(String state, String logmessage, ConnectionStatus level) { - this.state = state; - this.logmessage = logmessage; - this.level = level; - } - } - - @Override - public void updateState (String state, String logmessage, int resid, ConnectionStatus level) { - mMostRecentState = new UpdateMessage(state, logmessage, level); - if (ProfileManager.getLastConnectedVpn()!=null) - mMostRecentState.vpnUUID = ProfileManager.getLastConnectedVpn().getUUIDString(); - - Message msg = mHandler.obtainMessage(SEND_TOALL, mMostRecentState); - msg.sendToTarget(); - - } - - private static final OpenVPNServiceHandler mHandler = new OpenVPNServiceHandler(); - - - static class OpenVPNServiceHandler extends Handler { - WeakReference service= null; - - private void setService(ExternalOpenVPNService eos) - { - service = new WeakReference(eos); - } - - @Override public void handleMessage(Message msg) { - - RemoteCallbackList callbacks; - switch (msg.what) { - case SEND_TOALL: - if(service ==null || service.get() == null) - return; - - callbacks = service.get().mCallbacks; - - - // Broadcast to all clients the new value. - final int N = callbacks.beginBroadcast(); - for (int i=0; i service = null; + + private void setService(ExternalOpenVPNService eos) { + service = new WeakReference(eos); + } + + @Override + public void handleMessage(Message msg) { + + RemoteCallbackList callbacks; + switch (msg.what) { + case SEND_TOALL: + if (service == null || service.get() == null) + return; + + callbacks = service.get().mCallbacks; + + + // Broadcast to all clients the new value. + final int N = callbacks.beginBroadcast(); + for (int i = 0; i < N; i++) { + try { + sendUpdate(callbacks.getBroadcastItem(i), (UpdateMessage) msg.obj); + } catch (RemoteException e) { + // The RemoteCallbackList will take care of removing + // the dead object for us. + } + } + callbacks.finishBroadcast(); + break; + } + } + + private void sendUpdate(IOpenVPNStatusCallback broadcastItem, + UpdateMessage um) throws RemoteException { + broadcastItem.newStatus(um.vpnUUID, um.state, um.logmessage, um.level.name()); + } + } } \ No newline at end of file diff --git a/src/de/blinkt/openvpn/core/OpenVPNThread.java b/src/de/blinkt/openvpn/core/OpenVPNThread.java index 0807b33d..f814ff29 100644 --- a/src/de/blinkt/openvpn/core/OpenVPNThread.java +++ b/src/de/blinkt/openvpn/core/OpenVPNThread.java @@ -137,6 +137,9 @@ public class OpenVPNThread implements Runnable { else if ((flags & M_DEBUG)!=0) logStatus = VpnStatus.LogLevel.VERBOSE; + if (msg.startsWith("MANAGEMENT: CMD")) + logLevel = Math.max(4, logLevel); + VpnStatus.logMessageOpenVPN(logStatus,logLevel,msg); } else { diff --git a/src/de/blinkt/openvpn/core/OpenVpnManagementThread.java b/src/de/blinkt/openvpn/core/OpenVpnManagementThread.java index 03b36144..abca275a 100644 --- a/src/de/blinkt/openvpn/core/OpenVpnManagementThread.java +++ b/src/de/blinkt/openvpn/core/OpenVpnManagementThread.java @@ -273,8 +273,12 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { } int ovpnlevel = Integer.parseInt(args[2]) & 0x0F; + String msg = args[3]; - VpnStatus.logMessageOpenVPN(level,ovpnlevel, args[3]); + if (msg.startsWith("MANAGEMENT: CMD")) + ovpnlevel = Math.max(4, ovpnlevel); + + VpnStatus.logMessageOpenVPN(level,ovpnlevel, msg); } private void handleHold() { @@ -387,7 +391,10 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { } else if (needed.equals("IFCONFIG6")) { mOpenVPNService.setLocalIPv6(extra); - } else if (needed.equals("OPENTUN")) { + } else if (needed.equals("PERSIST_TUN_ACTION")) { + // check if tun cfg stayed the same + status = mOpenVPNService.getTunReopenStatus(); + } else if (needed.equals("OPENTUN")) { if(sendTunFD(needed,extra)) return; else diff --git a/src/de/blinkt/openvpn/core/OpenVpnService.java b/src/de/blinkt/openvpn/core/OpenVpnService.java index e4437d2c..ab21fc1b 100644 --- a/src/de/blinkt/openvpn/core/OpenVpnService.java +++ b/src/de/blinkt/openvpn/core/OpenVpnService.java @@ -14,6 +14,7 @@ import android.net.VpnService; import android.os.*; import android.os.Handler.Callback; import android.preference.PreferenceManager; +import android.text.TextUtils; import de.blinkt.openvpn.DisconnectVPN; import de.blinkt.openvpn.LogWindow; import de.blinkt.openvpn.R; @@ -24,6 +25,7 @@ import de.blinkt.openvpn.core.VpnStatus.StateListener; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.security.MessageDigest; import java.util.HashMap; import java.util.Locale; import java.util.Vector; @@ -55,6 +57,7 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac private long mConnecttime; private boolean mOvpn3 = false; private OpenVPNManagement mManagement; + private String mLastTunCfg; // From: http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java public static String humanReadableByteCount(long bytes, boolean mbit) { @@ -386,6 +389,24 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac } + private String getTunConfigString() + { + // The format of the string is not important, only that + // two identical configurations produce the same result + String cfg="TUNCFG UNQIUE STRING ips:"; + + if (mLocalIP!=null) + cfg+=mLocalIP.toString(); + if (mLocalIPv6!=null) + cfg+=mLocalIPv6.toString(); + + cfg+= "routes: " + TextUtils.join("|",mRoutes) + TextUtils.join("|",mRoutesv6); + cfg+= "dns: " + TextUtils.join("|",mDnslist); + cfg+= "domain: " + mDomain; + cfg+= "mtu: " + mMtu; + return cfg; + } + public ParcelFileDescriptor openTun() { Builder builder = new Builder(); @@ -465,6 +486,8 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac if (mDnslist.size() == 0) VpnStatus.logInfo(R.string.warn_no_dns); + mLastTunCfg = getTunConfigString(); + // Reset information mDnslist.clear(); mRoutes.clear(); @@ -625,6 +648,16 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac return mManagement; } + public String getTunReopenStatus() { + String currentConfiguration = getTunConfigString(); + if(currentConfiguration.equals(mLastTunCfg)) + return "NOACTION"; + else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) + return "OPEN_AFTER_CLOSE"; + else + return "OPEN_BEFORE_CLOSE"; + } + public class LocalBinder extends Binder { public OpenVpnService getService() { // Return this instance of LocalService so clients can call public methods -- cgit v1.2.3