diff options
| author | Arne Schwabe <arne@rfc2549.org> | 2016-02-29 14:37:51 +0100 | 
|---|---|---|
| committer | Arne Schwabe <arne@rfc2549.org> | 2016-02-29 14:37:51 +0100 | 
| commit | 5e078020bfc7a49c8d6011c3c4c184d5593df40a (patch) | |
| tree | 18bb17fb52d305d708982be99a58df9e7c71370f /main/src | |
| parent | b3e20603bb687da8a348f1c73d9645f86d159483 (diff) | |
Refactor the way the VPN is launched, move more of the responsibility to the OpenVPNService.
Diffstat (limited to 'main/src')
11 files changed, 430 insertions, 426 deletions
diff --git a/main/src/main/AndroidManifest.xml b/main/src/main/AndroidManifest.xml index a6ec8b0a..d4bb0674 100644 --- a/main/src/main/AndroidManifest.xml +++ b/main/src/main/AndroidManifest.xml @@ -146,7 +146,7 @@              android:noHistory="true"              android:label="@string/vpn_launch_title"              android:taskAffinity="" -            android:theme="@android:style/Theme.NoDisplay" +            android:theme="@style/blinkt.dialog"              tools:ignore="ExportedActivity">              <intent-filter>                  <action android:name="android.intent.action.MAIN" /> diff --git a/main/src/main/java/de/blinkt/openvpn/LaunchVPN.java b/main/src/main/java/de/blinkt/openvpn/LaunchVPN.java index 7ac5cc11..26135eac 100644 --- a/main/src/main/java/de/blinkt/openvpn/LaunchVPN.java +++ b/main/src/main/java/de/blinkt/openvpn/LaunchVPN.java @@ -34,122 +34,117 @@ import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus;  /**   * This Activity actually handles two stages of a launcher shortcut's life cycle. - *  + * <p/>   * 1. Your application offers to provide shortcuts to the launcher.  When - *    the user installs a shortcut, an activity within your application - *    generates the actual shortcut and returns it to the launcher, where it - *    is shown to the user as an icon. - * + * the user installs a shortcut, an activity within your application + * generates the actual shortcut and returns it to the launcher, where it + * is shown to the user as an icon. + * <p/>   * 2. Any time the user clicks on an installed shortcut, an intent is sent. - *    Typically this would then be handled as necessary by an activity within - *    your application. - *     + * Typically this would then be handled as necessary by an activity within + * your application. + * <p/>   * We handle stage 1 (creating a shortcut) by simply sending back the information (in the form   * of an {@link android.content.Intent} that the launcher will use to create the shortcut. - *  + * <p/>   * You can also implement this in an interactive way, by having your activity actually present   * UI for the user to select the specific nature of the shortcut, such as a contact, picture, URL,   * media item, or action. - *  + * <p/>   * We handle stage 2 (responding to a shortcut) in this sample by simply displaying the contents   * of the incoming {@link android.content.Intent}. - *  + * <p/>   * In a real application, you would probably use the shortcut intent to display specific content   * or start a particular operation.   */  public class LaunchVPN extends Activity { -	public static final String EXTRA_KEY = "de.blinkt.openvpn.shortcutProfileUUID"; -	public static final String EXTRA_NAME = "de.blinkt.openvpn.shortcutProfileName"; -	public static final String EXTRA_HIDELOG =  "de.blinkt.openvpn.showNoLogWindow"; -	public static final String CLEARLOG = "clearlogconnect"; +    public static final String EXTRA_KEY = "de.blinkt.openvpn.shortcutProfileUUID"; +    public static final String EXTRA_NAME = "de.blinkt.openvpn.shortcutProfileName"; +    public static final String EXTRA_HIDELOG = "de.blinkt.openvpn.showNoLogWindow"; +    public static final String CLEARLOG = "clearlogconnect"; -	private static final int START_VPN_PROFILE= 70; +    private static final int START_VPN_PROFILE = 70; -	private ProfileManager mPM; -	private VpnProfile mSelectedProfile; -	private boolean mhideLog=false; +    private VpnProfile mSelectedProfile; +    private boolean mhideLog = false; -	private boolean mCmfixed=false; +    private boolean mCmfixed = false; -	@Override -	public void onCreate(Bundle icicle) { -		super.onCreate(icicle); +    @Override +    public void onCreate(Bundle icicle) { +        super.onCreate(icicle); -		mPM =ProfileManager.getInstance(this); -		startVpnFromIntent(); -		finish(); -	}	 +        startVpnFromIntent(); +    } -	protected void startVpnFromIntent() { -		// Resolve the intent +    protected void startVpnFromIntent() { +        // Resolve the intent -		final Intent intent = getIntent(); -		final String action = intent.getAction(); +        final Intent intent = getIntent(); +        final String action = intent.getAction(); -		// If the intent is a request to create a shortcut, we'll do that and exit +        // If the intent is a request to create a shortcut, we'll do that and exit -		if(Intent.ACTION_MAIN.equals(action)) { -			// Check if we need to clear the log -			if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(CLEARLOG, true)) -				VpnStatus.clearLog(); +        if (Intent.ACTION_MAIN.equals(action)) { +            // Check if we need to clear the log +            if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(CLEARLOG, true)) +                VpnStatus.clearLog(); -			// we got called to be the starting point, most likely a shortcut -			String shortcutUUID = intent.getStringExtra( EXTRA_KEY); -			String shortcutName = intent.getStringExtra( EXTRA_NAME); -			mhideLog = intent.getBooleanExtra(EXTRA_HIDELOG, false); +            // we got called to be the starting point, most likely a shortcut +            String shortcutUUID = intent.getStringExtra(EXTRA_KEY); +            String shortcutName = intent.getStringExtra(EXTRA_NAME); +            mhideLog = intent.getBooleanExtra(EXTRA_HIDELOG, false); -			VpnProfile profileToConnect = ProfileManager.get(this,shortcutUUID); -			if(shortcutName != null && profileToConnect ==null) -				profileToConnect = ProfileManager.getInstance(this).getProfileByName(shortcutName); +            VpnProfile profileToConnect = ProfileManager.get(this, shortcutUUID); +            if (shortcutName != null && profileToConnect == null) +                profileToConnect = ProfileManager.getInstance(this).getProfileByName(shortcutName); -			if(profileToConnect ==null) { -				VpnStatus.logError(R.string.shortcut_profile_notfound); -				// show Log window to display error -				showLogWindow(); -				finish(); -				return; -			} - -			mSelectedProfile = profileToConnect; -			launchVPN(); - -		} -	} +            if (profileToConnect == null) { +                VpnStatus.logError(R.string.shortcut_profile_notfound); +                // show Log window to display error +                showLogWindow(); +                finish(); +            } else { +                mSelectedProfile = profileToConnect; +                launchVPN(); +            } +        } +    } -	private void askForPW(final int type) { +    private void askForPW(final int type) { -		final EditText entry = new EditText(this); +        final EditText entry = new EditText(this);          final View userpwlayout = getLayoutInflater().inflate(R.layout.userpass, null, false); -		entry.setSingleLine(); -		entry.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); -		entry.setTransformationMethod(new PasswordTransformationMethod()); +        entry.setSingleLine(); +        entry.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); +        entry.setTransformationMethod(new PasswordTransformationMethod()); -		AlertDialog.Builder dialog = new AlertDialog.Builder(this); -		dialog.setTitle(getString(R.string.pw_request_dialog_title, getString(type))); -		dialog.setMessage(getString(R.string.pw_request_dialog_prompt, mSelectedProfile.mName)); +        AlertDialog.Builder dialog = new AlertDialog.Builder(this); +        dialog.setTitle(getString(R.string.pw_request_dialog_title, getString(type))); +        dialog.setMessage(getString(R.string.pw_request_dialog_prompt, mSelectedProfile.mName));          if (type == R.string.password) { -            ((EditText)userpwlayout.findViewById(R.id.username)).setText(mSelectedProfile.mUsername); -            ((EditText)userpwlayout.findViewById(R.id.password)).setText(mSelectedProfile.mPassword); -            ((CheckBox)userpwlayout.findViewById(R.id.save_password)).setChecked(!TextUtils.isEmpty(mSelectedProfile.mPassword)); -            ((CheckBox)userpwlayout.findViewById(R.id.show_password)).setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { +            ((EditText) userpwlayout.findViewById(R.id.username)).setText(mSelectedProfile.mUsername); +            ((EditText) userpwlayout.findViewById(R.id.password)).setText(mSelectedProfile.mPassword); +            ((CheckBox) userpwlayout.findViewById(R.id.save_password)).setChecked(!TextUtils.isEmpty(mSelectedProfile.mPassword)); +            ((CheckBox) userpwlayout.findViewById(R.id.show_password)).setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {                  @Override                  public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {                      if (isChecked) -                        ((EditText)userpwlayout.findViewById(R.id.password)).setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); +                        ((EditText) userpwlayout.findViewById(R.id.password)).setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);                      else -                        ((EditText)userpwlayout.findViewById(R.id.password)).setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); +                        ((EditText) userpwlayout.findViewById(R.id.password)).setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);                  }              });              dialog.setView(userpwlayout);          } else { -    		dialog.setView(entry); +            dialog.setView(entry);          }          AlertDialog.Builder builder = dialog.setPositiveButton(android.R.string.ok, @@ -162,9 +157,9 @@ public class LaunchVPN extends Activity {                              String pw = ((EditText) userpwlayout.findViewById(R.id.password)).getText().toString();                              if (((CheckBox) userpwlayout.findViewById(R.id.save_password)).isChecked()) { -                                 mSelectedProfile.mPassword=pw; +                                mSelectedProfile.mPassword = pw;                              } else { -                                mSelectedProfile.mPassword=null; +                                mSelectedProfile.mPassword = null;                                  mSelectedProfile.mTransientPW = pw;                              }                          } else { @@ -176,133 +171,119 @@ public class LaunchVPN extends Activity {                  });          dialog.setNegativeButton(android.R.string.cancel, -				new DialogInterface.OnClickListener() { -			@Override -			public void onClick(DialogInterface dialog, int which) { -				VpnStatus.updateStateString("USER_VPN_PASSWORD_CANCELLED", "", R.string.state_user_vpn_password_cancelled, -                        ConnectionStatus.LEVEL_NOTCONNECTED); -				finish(); -			} -		}); - -		dialog.create().show(); - -	} -	@Override -	protected void onActivityResult (int requestCode, int resultCode, Intent data) { -		super.onActivityResult(requestCode, resultCode, data); - -		if(requestCode==START_VPN_PROFILE) { -			if(resultCode == Activity.RESULT_OK) { -				int needpw = mSelectedProfile.needUserPWInput(false); -				if(needpw !=0) { -					VpnStatus.updateStateString("USER_VPN_PASSWORD", "", R.string.state_user_vpn_password, -                            ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT); -					askForPW(needpw); -				} else { -					SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);         -					boolean showLogWindow = prefs.getBoolean("showlogwindow", true); - -					if(!mhideLog && showLogWindow) -						showLogWindow(); -					new startOpenVpnThread().start(); -				} -			} else if (resultCode == Activity.RESULT_CANCELED) { -				// User does not want us to start, so we just vanish -				VpnStatus.updateStateString("USER_VPN_PERMISSION_CANCELLED", "", R.string.state_user_vpn_permission_cancelled, -                        ConnectionStatus.LEVEL_NOTCONNECTED); - -				finish(); -			} -		} -	} -	void showLogWindow() { +                new DialogInterface.OnClickListener() { +                    @Override +                    public void onClick(DialogInterface dialog, int which) { +                        VpnStatus.updateStateString("USER_VPN_PASSWORD_CANCELLED", "", R.string.state_user_vpn_password_cancelled, +                                ConnectionStatus.LEVEL_NOTCONNECTED); +                        finish(); +                    } +                }); -		Intent startLW = new Intent(getBaseContext(),LogWindow.class); -		startLW.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); -		startActivity(startLW); +        dialog.create().show(); -	} +    } -	void showConfigErrorDialog(int vpnok) { -		AlertDialog.Builder d = new AlertDialog.Builder(this); -		d.setTitle(R.string.config_error_found); -		d.setMessage(vpnok); -		d.setPositiveButton(android.R.string.ok, new OnClickListener() { +    @Override +    protected void onActivityResult(int requestCode, int resultCode, Intent data) { +        super.onActivityResult(requestCode, resultCode, data); -			@Override -			public void onClick(DialogInterface dialog, int which) { -				finish(); +        if (requestCode == START_VPN_PROFILE) { +            if (resultCode == Activity.RESULT_OK) { +                int needpw = mSelectedProfile.needUserPWInput(false); +                if (needpw != 0) { +                    VpnStatus.updateStateString("USER_VPN_PASSWORD", "", R.string.state_user_vpn_password, +                            ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT); +                    askForPW(needpw); +                } else { +                    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); +                    boolean showLogWindow = prefs.getBoolean("showlogwindow", true); + +                    if (!mhideLog && showLogWindow) +                        showLogWindow(); +                    VPNLaunchHelper.startOpenVpn(mSelectedProfile, getBaseContext()); +                    finish(); +                } +            } else if (resultCode == Activity.RESULT_CANCELED) { +                // User does not want us to start, so we just vanish +                VpnStatus.updateStateString("USER_VPN_PERMISSION_CANCELLED", "", R.string.state_user_vpn_permission_cancelled, +                        ConnectionStatus.LEVEL_NOTCONNECTED); -			} -		}); -		d.show(); -	} +                finish(); +            } +        } +    } -	void launchVPN () { -		int vpnok = mSelectedProfile.checkProfile(this); -		if(vpnok!= R.string.no_error_found) { -			showConfigErrorDialog(vpnok); -			return; -		} +    void showLogWindow() { -		Intent intent = VpnService.prepare(this); -		// Check if we want to fix /dev/tun -		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);         -		boolean usecm9fix = prefs.getBoolean("useCM9Fix", false); -		boolean loadTunModule = prefs.getBoolean("loadTunModule", false); +        Intent startLW = new Intent(getBaseContext(), LogWindow.class); +        startLW.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); +        startActivity(startLW); -		if(loadTunModule) -			execeuteSUcmd("insmod /system/lib/modules/tun.ko"); +    } -		if(usecm9fix && !mCmfixed ) { -			execeuteSUcmd("chown system /dev/tun"); -		} +    void showConfigErrorDialog(int vpnok) { +        AlertDialog.Builder d = new AlertDialog.Builder(this); +        d.setTitle(R.string.config_error_found); +        d.setMessage(vpnok); +        d.setPositiveButton(android.R.string.ok, new OnClickListener() { +            @Override +            public void onClick(DialogInterface dialog, int which) { +                finish(); -		if (intent != null) { -			VpnStatus.updateStateString("USER_VPN_PERMISSION", "", R.string.state_user_vpn_permission, -                    ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT); -			// Start the query -			try { -				startActivityForResult(intent, START_VPN_PROFILE); -			} catch (ActivityNotFoundException ane) { -				// Shame on you Sony! At least one user reported that  -				// an official Sony Xperia Arc S image triggers this exception -				VpnStatus.logError(R.string.no_vpn_support_image); -				showLogWindow(); -			} -		} else { -			onActivityResult(START_VPN_PROFILE, Activity.RESULT_OK, null); -		} - -	} - -	private void execeuteSUcmd(String command) { -		ProcessBuilder pb = new ProcessBuilder("su","-c",command); -		try { -			Process p = pb.start(); -			int ret = p.waitFor(); -			if(ret ==0) -				mCmfixed=true; -		} catch (InterruptedException e) { -            VpnStatus.logException("SU command", e); +            } +        }); +        d.show(); +    } -		} catch (IOException e) { -            VpnStatus.logException("SU command", e); -		} -	} +    void launchVPN() { +        int vpnok = mSelectedProfile.checkProfile(this); +        if (vpnok != R.string.no_error_found) { +            showConfigErrorDialog(vpnok); +            return; +        } -	private class startOpenVpnThread extends Thread { +        Intent intent = VpnService.prepare(this); +        // Check if we want to fix /dev/tun +        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); +        boolean usecm9fix = prefs.getBoolean("useCM9Fix", false); +        boolean loadTunModule = prefs.getBoolean("loadTunModule", false); -		@Override -		public void run() { -			VPNLaunchHelper.startOpenVpn(mSelectedProfile, getBaseContext()); -			finish(); +        if (loadTunModule) +            execeuteSUcmd("insmod /system/lib/modules/tun.ko"); -		} +        if (usecm9fix && !mCmfixed) { +            execeuteSUcmd("chown system /dev/tun"); +        } -	} +        if (intent != null) { +            VpnStatus.updateStateString("USER_VPN_PERMISSION", "", R.string.state_user_vpn_permission, +                    ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT); +            // Start the query +            try { +                startActivityForResult(intent, START_VPN_PROFILE); +            } catch (ActivityNotFoundException ane) { +                // Shame on you Sony! At least one user reported that +                // an official Sony Xperia Arc S image triggers this exception +                VpnStatus.logError(R.string.no_vpn_support_image); +                showLogWindow(); +            } +        } else { +            onActivityResult(START_VPN_PROFILE, Activity.RESULT_OK, null); +        } +    } +    private void execeuteSUcmd(String command) { +        try { +            ProcessBuilder pb = new ProcessBuilder("su", "-c", command); +            Process p = pb.start(); +            int ret = p.waitFor(); +            if (ret == 0) +                mCmfixed = true; +        } catch (InterruptedException | IOException e) { +            VpnStatus.logException("SU command", e); +        } +    }  } diff --git a/main/src/main/java/de/blinkt/openvpn/VpnProfile.java b/main/src/main/java/de/blinkt/openvpn/VpnProfile.java index 5ef95b6e..9d4e8588 100644 --- a/main/src/main/java/de/blinkt/openvpn/VpnProfile.java +++ b/main/src/main/java/de/blinkt/openvpn/VpnProfile.java @@ -62,7 +62,7 @@ public class VpnProfile implements Serializable, Cloneable {      // The Serializable documentation mentions that class name change are possible      // but the how is unclear      // -    transient public static final long MAX_EMBED_FILE_SIZE = 2048*1024; // 2048kB +    transient public static final long MAX_EMBED_FILE_SIZE = 2048 * 1024; // 2048kB      // Don't change this, not all parts of the program use this constant      public static final String EXTRA_PROFILEUUID = "de.blinkt.openvpn.profileUUID";      public static final String INLINE_TAG = "[[INLINE]]"; @@ -148,15 +148,15 @@ public class VpnProfile implements Serializable, Cloneable {      private int mProfileVersion;      public String mExcludedRoutes;      public String mExcludedRoutesv6; -    public int mMssFix =0; // -1 is default, +    public int mMssFix = 0; // -1 is default,      public Connection[] mConnections = new Connection[0]; -    public boolean mRemoteRandom=false; +    public boolean mRemoteRandom = false;      public HashSet<String> mAllowedAppsVpn = new HashSet<>();      public boolean mAllowedAppsVpnAreDisallowed = true;      public String mProfileCreator; -    public boolean mPushPeerInfo=false; +    public boolean mPushPeerInfo = false;      public static final boolean mIsOpenVPN22 = false;      /* Options no longer used in new profiles */ @@ -170,7 +170,7 @@ public class VpnProfile implements Serializable, Cloneable {          mProfileVersion = CURRENT_PROFILE_VERSION;          mConnections = new Connection[1]; -        mConnections[0]  = new Connection(); +        mConnections[0] = new Connection();      }      public static String openVpnEscape(String unescaped) { @@ -198,7 +198,7 @@ public class VpnProfile implements Serializable, Cloneable {          mCheckRemoteCN = false;          mPersistTun = false;          mAllowLocalLAN = true; -        mPushPeerInfo =false; +        mPushPeerInfo = false;          mMssFix = 0;      } @@ -208,33 +208,33 @@ public class VpnProfile implements Serializable, Cloneable {      }      public String getName() { -        if (mName==null) +        if (mName == null)              return "No profile name";          return mName;      } -    public void upgradeProfile(){ -        if(mProfileVersion< 2) { +    public void upgradeProfile() { +        if (mProfileVersion < 2) {              /* default to the behaviour the OS used */              mAllowLocalLAN = Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT;          }          if (mProfileVersion < 4) {              moveOptionsToConnection(); -            mAllowedAppsVpnAreDisallowed=true; +            mAllowedAppsVpnAreDisallowed = true;          } -        if (mAllowedAppsVpn==null) +        if (mAllowedAppsVpn == null)              mAllowedAppsVpn = new HashSet<>(); -        if (mConnections ==null) +        if (mConnections == null)              mConnections = new Connection[0];          if (mProfileVersion < 6) {              if (TextUtils.isEmpty(mProfileCreator)) -                mUserEditable=true; +                mUserEditable = true;          } -        mProfileVersion= CURRENT_PROFILE_VERSION; +        mProfileVersion = CURRENT_PROFILE_VERSION;      } @@ -272,7 +272,7 @@ public class VpnProfile implements Serializable, Cloneable {              cfg += String.format("setenv IV_GUI_VER %s \n", openVpnEscape(getVersionEnvString(context)));              String versionString = String.format("%d %s %s %s %s %s", Build.VERSION.SDK_INT, Build.VERSION.RELEASE,                      NativeUtils.getNativeAPI(), Build.BRAND, Build.BOARD, Build.MODEL); -            cfg += String.format("setenv IV_PLAT_VER %s\n", openVpnEscape(versionString)) ; +            cfg += String.format("setenv IV_PLAT_VER %s\n", openVpnEscape(versionString));          }          cfg += "machine-readable-output\n"; @@ -317,7 +317,7 @@ public class VpnProfile implements Serializable, Cloneable {          boolean canUsePlainRemotes = true; -        if (mConnections.length==1) { +        if (mConnections.length == 1) {              cfg += mConnections[0].getConnectionBlock();          } else {              for (Connection conn : mConnections) { @@ -325,7 +325,7 @@ public class VpnProfile implements Serializable, Cloneable {              }              if (mRemoteRandom) -                cfg+="remote-random\n"; +                cfg += "remote-random\n";              if (canUsePlainRemotes) {                  for (Connection conn : mConnections) { @@ -413,13 +413,12 @@ public class VpnProfile implements Serializable, Cloneable {          if (mUseDefaultRoute)              routes += "route 0.0.0.0 0.0.0.0 vpn_gateway\n"; -        else -        { +        else {              for (String route : getCustomRoutes(mCustomRoutes)) {                  routes += "route " + route + " vpn_gateway\n";              } -            for (String route: getCustomRoutes(mExcludedRoutes)) { +            for (String route : getCustomRoutes(mExcludedRoutes)) {                  routes += "route " + route + " net_gateway\n";              }          } @@ -444,11 +443,11 @@ public class VpnProfile implements Serializable, Cloneable {          } -        if (mMssFix !=0){ -            if (mMssFix!=1450) { +        if (mMssFix != 0) { +            if (mMssFix != 1450) {                  cfg += String.format("mssfix %d\n", mMssFix, Locale.US);              } else -                cfg+="mssfix\n"; +                cfg += "mssfix\n";          }          if (mNobind) @@ -509,7 +508,7 @@ public class VpnProfile implements Serializable, Cloneable {          }          if (mPushPeerInfo) -            cfg+="push-peer-info\n"; +            cfg += "push-peer-info\n";          SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);          boolean usesystemproxy = prefs.getBoolean("usesystemproxy", true); @@ -539,8 +538,6 @@ public class VpnProfile implements Serializable, Cloneable {          } - -          return cfg;      } @@ -630,39 +627,32 @@ public class VpnProfile implements Serializable, Cloneable {      } - -      public Intent prepareStartService(Context context) {          Intent intent = getStartServiceIntent(context); +        // TODO: Handle this?! +//        if (mAuthenticationType == VpnProfile.TYPE_KEYSTORE || mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE) { +//            if (getKeyStoreCertificates(context) == null) +//                return null; +//        } -        if (mAuthenticationType == VpnProfile.TYPE_KEYSTORE || mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE) { -            if (getKeyStoreCertificates(context) == null) -                return null; -        } - +        return intent; +    } -        try { -            FileWriter cfg = new FileWriter(VPNLaunchHelper.getConfigFilePath(context)); -            cfg.write(getConfigFile(context, false)); -            cfg.flush(); -            cfg.close(); -        } catch (IOException e) { -            VpnStatus.logException(e); -        } +    public void writeConfigFile(Context context) throws IOException { +        FileWriter cfg = new FileWriter(VPNLaunchHelper.getConfigFilePath(context)); +        cfg.write(getConfigFile(context, false)); +        cfg.flush(); +        cfg.close(); -        return intent;      }      public Intent getStartServiceIntent(Context context) {          String prefix = context.getPackageName();          Intent intent = new Intent(context, OpenVPNService.class); -        intent.putExtra(prefix + ".ARGV", VPNLaunchHelper.buildOpenvpnArgv(context));          intent.putExtra(prefix + ".profileUUID", mUuid.toString()); -        ApplicationInfo info = context.getApplicationInfo(); -        intent.putExtra(prefix + ".nativelib", info.nativeLibraryDir);          return intent;      } @@ -673,11 +663,10 @@ public class VpnProfile implements Serializable, Cloneable {      public static String getDisplayName(String embeddedFile) {          int start = DISPLAYNAME_TAG.length();          int end = embeddedFile.indexOf(INLINE_TAG); -        return embeddedFile.substring(start,end); +        return embeddedFile.substring(start, end);      } -    public static String getEmbeddedContent(String data) -    { +    public static String getEmbeddedContent(String data) {          if (!data.contains(INLINE_TAG))              return data; @@ -686,7 +675,7 @@ public class VpnProfile implements Serializable, Cloneable {      }      public static boolean isEmbedded(String data) { -        if (data==null) +        if (data == null)              return false;          if (data.startsWith(INLINE_TAG) || data.startsWith(DISPLAYNAME_TAG))              return true; @@ -698,8 +687,8 @@ public class VpnProfile implements Serializable, Cloneable {          /* This method is called when OpenVPNService is restarted */          if ((mAuthenticationType == VpnProfile.TYPE_KEYSTORE || mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE) -                && mPrivateKey==null) { -            new Thread( new Runnable() { +                && mPrivateKey == null) { +            new Thread(new Runnable() {                  @Override                  public void run() {                      getKeyStoreCertificates(context); @@ -714,9 +703,9 @@ public class VpnProfile implements Serializable, Cloneable {          VpnProfile copy = (VpnProfile) super.clone();          copy.mUuid = UUID.randomUUID();          copy.mConnections = new Connection[mConnections.length]; -        int i=0; -        for (Connection conn: mConnections) { -            copy.mConnections[i++]=conn.clone(); +        int i = 0; +        for (Connection conn : mConnections) { +            copy.mConnections[i++] = conn.clone();          }          copy.mAllowedAppsVpn = (HashSet<String>) mAllowedAppsVpn.clone();          return copy; @@ -736,12 +725,12 @@ public class VpnProfile implements Serializable, Cloneable {      class NoCertReturnedException extends Exception { -        public NoCertReturnedException (String msg) { +        public NoCertReturnedException(String msg) {              super(msg);          }      } -    synchronized String[] getKeyStoreCertificates(Context context,int tries) { +    synchronized String[] getKeyStoreCertificates(Context context, int tries) {          try {              PrivateKey privateKey = KeyChain.getPrivateKey(context, mAlias);              mPrivateKey = privateKey; @@ -750,7 +739,7 @@ public class VpnProfile implements Serializable, Cloneable {              X509Certificate[] caChain = KeyChain.getCertificateChain(context, mAlias); -            if(caChain == null) +            if (caChain == null)                  throw new NoCertReturnedException("No certificate returned from Keystore");              if (caChain.length <= 1 && TextUtils.isEmpty(mCaFilename)) { @@ -775,10 +764,10 @@ public class VpnProfile implements Serializable, Cloneable {                      StringWriter caoutWriter = new StringWriter();                      PemWriter pw = new PemWriter(caoutWriter); -                    for (Certificate cert: cacerts) +                    for (Certificate cert : cacerts)                          pw.writeObject(new PemObject("CERTIFICATE", cert.getEncoded()));                      pw.close(); -                    caout= caoutWriter.toString(); +                    caout = caoutWriter.toString();                  } catch (Exception e) {                      VpnStatus.logError("Could not read CA certificate" + e.getLocalizedMessage()); @@ -801,12 +790,12 @@ public class VpnProfile implements Serializable, Cloneable {              String ca, extra; -            if(caout==null) { -                ca =keystoreChain; -                extra=null; +            if (caout == null) { +                ca = keystoreChain; +                extra = null;              } else {                  ca = caout; -                extra=keystoreChain; +                extra = keystoreChain;              }              return new String[]{ca, extra, user}; @@ -824,15 +813,15 @@ public class VpnProfile implements Serializable, Cloneable {              return null;          } catch (AssertionError e) { -            if (tries ==0) +            if (tries == 0)                  return null; -            VpnStatus.logError(String.format("Failure getting Keystore Keys (%s), retrying",e.getLocalizedMessage())); +            VpnStatus.logError(String.format("Failure getting Keystore Keys (%s), retrying", e.getLocalizedMessage()));              try {                  Thread.sleep(3000);              } catch (InterruptedException e1) {                  VpnStatus.logException(e1);              } -            return getKeyStoreCertificates(context, tries-1); +            return getKeyStoreCertificates(context, tries - 1);          }      } @@ -849,10 +838,10 @@ public class VpnProfile implements Serializable, Cloneable {                  return R.string.ipv4_format_error;          }          if (!mUseDefaultRoute) { -            if (!TextUtils.isEmpty(mCustomRoutes) &&  getCustomRoutes(mCustomRoutes).size() == 0 ) +            if (!TextUtils.isEmpty(mCustomRoutes) && getCustomRoutes(mCustomRoutes).size() == 0)                  return R.string.custom_route_format_error; -            if (!TextUtils.isEmpty(mExcludedRoutes) &&  getCustomRoutes(mExcludedRoutes).size() == 0 ) +            if (!TextUtils.isEmpty(mExcludedRoutes) && getCustomRoutes(mExcludedRoutes).size() == 0)                  return R.string.custom_route_format_error;          } @@ -865,17 +854,16 @@ public class VpnProfile implements Serializable, Cloneable {              return R.string.missing_certificates;          if (!(mAuthenticationType == TYPE_KEYSTORE || mAuthenticationType == TYPE_USERPASS_KEYSTORE) -            && TextUtils.isEmpty(mCaFilename)) +                && TextUtils.isEmpty(mCaFilename))              return R.string.missing_ca_certificate; -          boolean noRemoteEnabled = true;          for (Connection c : mConnections)              if (c.mEnabled)                  noRemoteEnabled = false; -        if(noRemoteEnabled) +        if (noRemoteEnabled)              return R.string.remote_no_server_selected;          // Everything okay @@ -970,7 +958,7 @@ public class VpnProfile implements Serializable, Cloneable {          if (isUserPWAuth() &&                  (TextUtils.isEmpty(mUsername) || -                (TextUtils.isEmpty(mPassword) && (mTransientPW == null  || ignoreTransient)))) { +                        (TextUtils.isEmpty(mPassword) && (mTransientPW == null || ignoreTransient)))) {              return R.string.password;          }          return 0; diff --git a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java index 2771fa6a..85c1d526 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java +++ b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java @@ -24,7 +24,10 @@ public interface OpenVPNManagement {  	void resume(); -	boolean stopVPN(); +    /** +     * @param replaceConnection True if the VPN is connected by a new connection. +     */ +	boolean stopVPN(boolean replaceConnection);      /*       * Rebind the interface diff --git a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java index 7dc93674..d2a21946 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -32,6 +32,7 @@ import android.text.TextUtils;  import android.util.Log;  import android.widget.Toast; +import java.io.IOException;  import java.lang.reflect.InvocationTargetException;  import java.lang.reflect.Method;  import java.net.Inet6Address; @@ -115,7 +116,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac      @Override      public void onRevoke() {          VpnStatus.logInfo(R.string.permission_revoked); -        mManagement.stopVPN(); +        mManagement.stopVPN(false);          endVpnService();      } @@ -184,13 +185,13 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac          startForeground(OPENVPN_STATUS, notification);          // Check if running on a TV -        if(runningOnAndroidTV() && !lowpriority) +        if (runningOnAndroidTV() && !lowpriority)              guiHandler.post(new Runnable() {                  @Override                  public void run() { -                    if (mlastToast!=null) +                    if (mlastToast != null)                          mlastToast.cancel();                      String toastText = String.format(Locale.getDefault(), "%s - %s", mProfile.mName, msg);                      mlastToast = Toast.makeText(getBaseContext(), toastText, Toast.LENGTH_SHORT); @@ -377,21 +378,49 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac              mProfile = ProfileManager.get(this, profileUUID);          } +        /* start the OpenVPN process itself in a background thread */ +        new Thread(new Runnable() { +            @Override +            public void run() { +                startOpenVPN(); +            } +        }).start(); + + +        ProfileManager.setConnectedVpnProfile(this, mProfile); +        /* TODO: At the moment we have no way to handle asynchronous PW input +         * Fixing will also allow to handle challenge/response authentication */ +        if (mProfile.needUserPWInput(true) != 0) +            return START_NOT_STICKY; + +        return START_STICKY; +    } + +    private void startOpenVPN() { +        VpnStatus.logInfo(R.string.building_configration); +        VpnStatus.updateStateString("VPN_GENERATE_CONFIG", "", R.string.building_configration, VpnStatus.ConnectionStatus.LEVEL_START); + + +        try { +            mProfile.writeConfigFile(this); +        } catch (IOException e) { +            VpnStatus.logException("Error writing config file", e); +            endVpnService(); +            return; +        }          // Extract information from the intent.          String prefix = getPackageName(); -        String[] argv = intent.getStringArrayExtra(prefix + ".ARGV"); -        String nativeLibraryDirectory = intent.getStringExtra(prefix + ".nativelib"); +        String nativeLibraryDirectory = getApplicationInfo().nativeLibraryDir; + +        // Also writes OpenVPN binary +        String[] argv = VPNLaunchHelper.buildOpenvpnArgv(this); -        String startTitle = getString(R.string.start_vpn_title, mProfile.mName); -        String startTicker = getString(R.string.start_vpn_ticker, mProfile.mName); -        showNotification(startTitle, startTicker, -                false, 0, LEVEL_CONNECTING_NO_SERVER_REPLY_YET);          // Set a flag that we are starting a new VPN          mStarting = true;          // Stop the previous session by interrupting the thread. -        if (mManagement != null && mManagement.stopVPN()) +        if (mManagement != null && mManagement.stopVPN(true))              // an old was asked to exit, wait 1s              try {                  Thread.sleep(1000); @@ -432,11 +461,11 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac                  mManagement = ovpnManagementThread;                  VpnStatus.logInfo("started Socket Thread");              } else { -                return START_NOT_STICKY; +                endVpnService(); +                return;              }          } -          Runnable processThread;          if (mOvpn3) { @@ -454,19 +483,16 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac              mProcessThread = new Thread(processThread, "OpenVPNProcessThread");              mProcessThread.start();          } -        if (mDeviceStateReceiver != null) -            unregisterDeviceStateReceiver(); - -        registerDeviceStateReceiver(mManagement); +        new Handler(getMainLooper()).post(new Runnable() { +            @Override +            public void run() { +                if (mDeviceStateReceiver != null) +                    unregisterDeviceStateReceiver(); -        ProfileManager.setConnectedVpnProfile(this, mProfile); -        /* TODO: At the moment we have no way to handle asynchronous PW input -         * Fixing will also allow to handle challenge/response authentication */ -        if (mProfile.needUserPWInput(true) != 0) -            return START_NOT_STICKY; - -        return START_STICKY; +                registerDeviceStateReceiver(mManagement); +            } +        });      }      private OpenVPNManagement instantiateOpenVPN3Core() { @@ -474,7 +500,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac              Class cl = Class.forName("de.blinkt.openvpn.core.OpenVPNThreadv3");              return (OpenVPNManagement) cl.getConstructor(OpenVPNService.class, VpnProfile.class).newInstance(this, mProfile);          } catch (IllegalArgumentException | InstantiationException | InvocationTargetException | -                NoSuchMethodException | ClassNotFoundException | IllegalAccessException e ) { +                NoSuchMethodException | ClassNotFoundException | IllegalAccessException e) {              e.printStackTrace();          }          return null; @@ -484,7 +510,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac      public void onDestroy() {          synchronized (mProcessLock) {              if (mProcessThread != null) { -                mManagement.stopVPN(); +                mManagement.stopVPN(true);              }          } @@ -580,7 +606,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac              // Check if the first DNS Server is in the VPN range              try {                  ipAddress dnsServer = new ipAddress(new CIDRIP(mDnslist.get(0), 32), true); -                boolean dnsIncluded=false; +                boolean dnsIncluded = false;                  for (ipAddress net : positiveIPv4Routes) {                      if (net.containsNet(dnsServer)) {                          dnsIncluded = true; @@ -619,8 +645,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac          } - -          if (mDomain != null)              builder.addSearchDomain(mDomain); @@ -715,7 +739,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac      @TargetApi(Build.VERSION_CODES.LOLLIPOP)      private void setAllowedVpnPackages(Builder builder) { -        boolean atLeastOneAllowedApp=false; +        boolean atLeastOneAllowedApp = false;          for (String pkg : mProfile.mAllowedAppsVpn) {              try {                  if (mProfile.mAllowedAppsVpnAreDisallowed) { diff --git a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java index baffaffc..fabe7e3e 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java +++ b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java @@ -34,51 +34,50 @@ public class OpenVPNThread implements Runnable {      @SuppressLint("SdCardPath")
      private static final String BROKEN_PIE_SUPPORT = "/data/data/de.blinkt.openvpn/cache/pievpn";
      private final static String BROKEN_PIE_SUPPORT2 = "syntax error";
 -	private static final String TAG = "OpenVPN";
 +    private static final String TAG = "OpenVPN";
      public static final int M_FATAL = (1 << 4);
      public static final int M_NONFATAL = (1 << 5);
      public static final int M_WARN = (1 << 6);
      public static final int M_DEBUG = (1 << 7);
      private String[] mArgv;
 -	private Process mProcess;
 -	private String mNativeDir;
 -	private OpenVPNService mService;
 -	private String mDumpPath;
 -	private Map<String, String> mProcessEnv;
 -    private boolean mBrokenPie=false;
 -
 -    public OpenVPNThread(OpenVPNService service,String[] argv, Map<String,String> processEnv, String nativelibdir)
 -	{
 -		mArgv = argv;
 -		mNativeDir = nativelibdir;
 -		mService = service;
 -		mProcessEnv = processEnv;
 -	}
 -	
 -	public void stopProcess() {
 -		mProcess.destroy();
 -	}
 -	
 -	@Override
 -	public void run() {
 -		try {
 -			Log.i(TAG, "Starting openvpn");			
 -			startOpenVPNThreadArgs(mArgv, mProcessEnv);
 -			Log.i(TAG, "Giving up");
 -		} catch (Exception e) {
 -            VpnStatus.logException("Starting OpenVPN Thread" ,e);
 -			Log.e(TAG, "OpenVPNThread Got " + e.toString());
 -		} finally {
 -			int exitvalue = 0;
 -			try {
 -				if (mProcess!=null)
 -					exitvalue = mProcess.waitFor();
 -			} catch ( IllegalThreadStateException ite) {
 -				VpnStatus.logError("Illegal Thread state: " + ite.getLocalizedMessage());
 -			} catch (InterruptedException ie) {
 -				VpnStatus.logError("InterruptedException: " + ie.getLocalizedMessage());
 -			}
 -			if( exitvalue != 0) {
 +    private Process mProcess;
 +    private String mNativeDir;
 +    private OpenVPNService mService;
 +    private String mDumpPath;
 +    private Map<String, String> mProcessEnv;
 +    private boolean mBrokenPie = false;
 +
 +    public OpenVPNThread(OpenVPNService service, String[] argv, Map<String, String> processEnv, String nativelibdir) {
 +        mArgv = argv;
 +        mNativeDir = nativelibdir;
 +        mService = service;
 +        mProcessEnv = processEnv;
 +    }
 +
 +    public void stopProcess() {
 +        mProcess.destroy();
 +    }
 +
 +    @Override
 +    public void run() {
 +        try {
 +            Log.i(TAG, "Starting openvpn");
 +            startOpenVPNThreadArgs(mArgv, mProcessEnv);
 +            Log.i(TAG, "Giving up");
 +        } catch (Exception e) {
 +            VpnStatus.logException("Starting OpenVPN Thread", e);
 +            Log.e(TAG, "OpenVPNThread Got " + e.toString());
 +        } finally {
 +            int exitvalue = 0;
 +            try {
 +                if (mProcess != null)
 +                    exitvalue = mProcess.waitFor();
 +            } catch (IllegalThreadStateException ite) {
 +                VpnStatus.logError("Illegal Thread state: " + ite.getLocalizedMessage());
 +            } catch (InterruptedException ie) {
 +                VpnStatus.logError("InterruptedException: " + ie.getLocalizedMessage());
 +            }
 +            if (exitvalue != 0) {
                  VpnStatus.logError("Process exited with exit value " + exitvalue);
                  if (mBrokenPie) {
                      /* This will probably fail since the NoPIE binary is probably not written */
 @@ -95,70 +94,70 @@ public class OpenVPNThread implements Runnable {                  }
              }
 -			
 -			VpnStatus.updateStateString("NOPROCESS", "No process running.", R.string.state_noprocess, ConnectionStatus.LEVEL_NOTCONNECTED);
 -			if(mDumpPath!=null) {
 -				try {
 -					BufferedWriter logout = new BufferedWriter(new FileWriter(mDumpPath + ".log"));
 -					SimpleDateFormat timeformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",Locale.GERMAN);
 -					for(LogItem li : VpnStatus.getlogbuffer()){
 -						String time = timeformat.format(new Date(li.getLogtime()));
 -						logout.write(time +" " + li.getString(mService) + "\n");
 -					}
 -					logout.close();
 -					VpnStatus.logError(R.string.minidump_generated);
 -				} catch (IOException e) {
 -					VpnStatus.logError("Writing minidump log: " + e.getLocalizedMessage());
 -				}
 -			}
 -
 -			mService.processDied();
 -			Log.i(TAG, "Exiting");
 -		}
 -	}
 -	
 -	private void startOpenVPNThreadArgs(String[] argv, Map<String, String> env) {
 -		LinkedList<String> argvlist = new LinkedList<String>();
 +
 +            VpnStatus.updateStateString("NOPROCESS", "No process running.", R.string.state_noprocess, ConnectionStatus.LEVEL_NOTCONNECTED);
 +            if (mDumpPath != null) {
 +                try {
 +                    BufferedWriter logout = new BufferedWriter(new FileWriter(mDumpPath + ".log"));
 +                    SimpleDateFormat timeformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.GERMAN);
 +                    for (LogItem li : VpnStatus.getlogbuffer()) {
 +                        String time = timeformat.format(new Date(li.getLogtime()));
 +                        logout.write(time + " " + li.getString(mService) + "\n");
 +                    }
 +                    logout.close();
 +                    VpnStatus.logError(R.string.minidump_generated);
 +                } catch (IOException e) {
 +                    VpnStatus.logError("Writing minidump log: " + e.getLocalizedMessage());
 +                }
 +            }
 +
 +            mService.processDied();
 +            Log.i(TAG, "Exiting");
 +        }
 +    }
 +
 +    private void startOpenVPNThreadArgs(String[] argv, Map<String, String> env) {
 +        LinkedList<String> argvlist = new LinkedList<String>();
          Collections.addAll(argvlist, argv);
 -	
 -		ProcessBuilder pb = new ProcessBuilder(argvlist);
 -		// Hack O rama
 -		
 -		String lbpath = genLibraryPath(argv, pb);
 -		
 -		pb.environment().put("LD_LIBRARY_PATH", lbpath);
 -
 -		// Add extra variables
 -		for(Entry<String,String> e:env.entrySet()){
 -			pb.environment().put(e.getKey(), e.getValue());
 -		}
 -		pb.redirectErrorStream(true);
 -		try {
 -			mProcess = pb.start();
 -			// Close the output, since we don't need it
 -			mProcess.getOutputStream().close();
 -			InputStream in = mProcess.getInputStream();
 -			BufferedReader br = new BufferedReader(new InputStreamReader(in));
 -				
 -			while(true) {
 -				String logline = br.readLine();
 -				if(logline==null) 
 -					return;
 -
 -				if (logline.startsWith(DUMP_PATH_STRING))
 -					mDumpPath = logline.substring(DUMP_PATH_STRING.length());
 +
 +        ProcessBuilder pb = new ProcessBuilder(argvlist);
 +        // Hack O rama
 +
 +        String lbpath = genLibraryPath(argv, pb);
 +
 +        pb.environment().put("LD_LIBRARY_PATH", lbpath);
 +
 +        // Add extra variables
 +        for (Entry<String, String> e : env.entrySet()) {
 +            pb.environment().put(e.getKey(), e.getValue());
 +        }
 +        pb.redirectErrorStream(true);
 +        try {
 +            mProcess = pb.start();
 +            // Close the output, since we don't need it
 +            mProcess.getOutputStream().close();
 +            InputStream in = mProcess.getInputStream();
 +            BufferedReader br = new BufferedReader(new InputStreamReader(in));
 +
 +            while (true) {
 +                String logline = br.readLine();
 +                if (logline == null)
 +                    return;
 +
 +                if (logline.startsWith(DUMP_PATH_STRING))
 +                    mDumpPath = logline.substring(DUMP_PATH_STRING.length());
                  if (logline.startsWith(BROKEN_PIE_SUPPORT) || logline.contains(BROKEN_PIE_SUPPORT2))
                      mBrokenPie = true;
 -					
 +
                  // 1380308330.240114 18000002 Send to HTTP proxy: 'X-Online-Host: bla.blabla.com'
                  Pattern p = Pattern.compile("(\\d+).(\\d+) ([0-9a-f])+ (.*)");
                  Matcher m = p.matcher(logline);
 -                if(m.matches()) {
 -                    int flags = Integer.parseInt(m.group(3),16);
 +                if (m.matches()) {
 +                    int flags = Integer.parseInt(m.group(3), 16);
                      String msg = m.group(4);
                      int logLevel = flags & 0x0F;
 @@ -166,45 +165,45 @@ public class OpenVPNThread implements Runnable {                      if ((flags & M_FATAL) != 0)
                          logStatus = VpnStatus.LogLevel.ERROR;
 -                    else if ((flags & M_NONFATAL)!=0)
 +                    else if ((flags & M_NONFATAL) != 0)
                          logStatus = VpnStatus.LogLevel.WARNING;
 -                    else if ((flags & M_WARN)!=0)
 +                    else if ((flags & M_WARN) != 0)
                          logStatus = VpnStatus.LogLevel.WARNING;
 -                    else if ((flags & M_DEBUG)!=0)
 +                    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);
 +                    VpnStatus.logMessageOpenVPN(logStatus, logLevel, msg);
                  } else {
                      VpnStatus.logInfo("P:" + logline);
                  }
 -			}
 -			
 -		
 -		} catch (IOException e) {
 -			VpnStatus.logException("Error reading from output of OpenVPN process" , e);
 -			stopProcess();
 -		}
 -		
 -		
 -	}
 -
 -	private String genLibraryPath(String[] argv, ProcessBuilder pb) {
 -		// Hack until I find a good way to get the real library path
 -		String applibpath = argv[0].replaceFirst("/cache/.*$"  , "/lib");
 -		
 -		String lbpath = pb.environment().get("LD_LIBRARY_PATH");
 -		if(lbpath==null) 
 -			lbpath = applibpath;
 -		else
 -			lbpath = applibpath + ":" + lbpath;
 -		
 -		if (!applibpath.equals(mNativeDir)) {
 -			lbpath =  mNativeDir + ":" + lbpath;
 -		}
 -		return lbpath;
 -	}
 +            }
 +
 +
 +        } catch (IOException e) {
 +            VpnStatus.logException("Error reading from output of OpenVPN process", e);
 +            stopProcess();
 +        }
 +
 +
 +    }
 +
 +    private String genLibraryPath(String[] argv, ProcessBuilder pb) {
 +        // Hack until I find a good way to get the real library path
 +        String applibpath = argv[0].replaceFirst("/cache/.*$", "/lib");
 +
 +        String lbpath = pb.environment().get("LD_LIBRARY_PATH");
 +        if (lbpath == null)
 +            lbpath = applibpath;
 +        else
 +            lbpath = applibpath + ":" + lbpath;
 +
 +        if (!applibpath.equals(mNativeDir)) {
 +            lbpath = mNativeDir + ":" + lbpath;
 +        }
 +        return lbpath;
 +    }
  }
 diff --git a/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java b/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java index 44426e97..1004ab00 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java +++ b/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java @@ -49,6 +49,7 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {      private pauseReason lastPauseReason = pauseReason.noNetwork;
      private PausedStateCallback mPauseCallback;
 +    private boolean mShuttingDown;
      public OpenVpnManagementThread(VpnProfile profile, OpenVPNService openVpnService) {
          mProfile = profile;
 @@ -223,7 +224,8 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {                      processByteCount(argument);
                      break;
                  case "STATE":
 -                    processState(argument);
 +                    if (!mShuttingDown)
 +                        processState(argument);
                      break;
                  case "PROXY":
                      processProxyCMD(argument);
 @@ -621,7 +623,9 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {      }
      @Override
 -    public boolean stopVPN() {
 +    public boolean stopVPN(boolean replaceConnection) {
 +        mShuttingDown = true;
          return stopOpenVPN();
      }
 +
  }
 diff --git a/main/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java b/main/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java index 7d0f6578..564ee1a4 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java +++ b/main/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java @@ -27,7 +27,7 @@ public class VPNLaunchHelper { -    static private String writeMiniVPN(Context context) { +    private static String writeMiniVPN(Context context) {          String[] abis;          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)              abis = getSupportedABIsLollipop(); @@ -73,12 +73,18 @@ public class VPNLaunchHelper {      } -    public static String[] buildOpenvpnArgv(Context c) { +    static String[] buildOpenvpnArgv(Context c) {          Vector<String> args = new Vector<>(); +        String binaryName = writeMiniVPN(c);          // Add fixed paramenters          //args.add("/data/data/de.blinkt.openvpn/lib/openvpn"); -        args.add(writeMiniVPN(c)); +        if(binaryName==null) { +            VpnStatus.logError("Error writing minivpn binary"); +            return null; +        } + +        args.add(binaryName);          args.add("--config");          args.add(getConfigFilePath(c)); @@ -126,14 +132,6 @@ public class VPNLaunchHelper {  	public static void startOpenVpn(VpnProfile startprofile, Context context) { -        VpnStatus.logInfo(R.string.building_configration); -        VpnStatus.updateStateString("VPN_GENERATE_CONFIG", "", R.string.building_configration, VpnStatus.ConnectionStatus.LEVEL_START); -        if(writeMiniVPN(context)==null) { -			VpnStatus.logError("Error writing minivpn binary"); -			return; -		} - -  		Intent startVPN = startprofile.prepareStartService(context);  		if(startVPN!=null)  			context.startService(startVPN); diff --git a/main/src/main/java/de/blinkt/openvpn/core/VpnStatus.java b/main/src/main/java/de/blinkt/openvpn/core/VpnStatus.java index 168dd080..bae94624 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/VpnStatus.java +++ b/main/src/main/java/de/blinkt/openvpn/core/VpnStatus.java @@ -622,8 +622,8 @@ public class VpnStatus {      public static synchronized void updateByteCount(long in, long out) {          long lastIn = mlastByteCount[0];          long lastOut = mlastByteCount[1]; -        long diffIn = mlastByteCount[2] = in - lastIn; -        long diffOut = mlastByteCount[3] = out - lastOut; +        long diffIn = mlastByteCount[2] = Math.max(0, in - lastIn); +        long diffOut = mlastByteCount[3] = Math.max(0, out - lastOut);          mlastByteCount = new long[]{in, out, diffIn, diffOut}; diff --git a/main/src/main/res/values-v21/styles.xml b/main/src/main/res/values-v21/styles.xml index 83a693f1..fa27ebe8 100644 --- a/main/src/main/res/values-v21/styles.xml +++ b/main/src/main/res/values-v21/styles.xml @@ -25,4 +25,5 @@          <item name="android:colorPrimaryDark">@color/primary_dark</item>          <item name="android:colorAccent">@color/accent</item>      </style> +  </resources> diff --git a/main/src/main/res/values/styles.xml b/main/src/main/res/values/styles.xml index 13d5e0b1..4cdf2c00 100644 --- a/main/src/main/res/values/styles.xml +++ b/main/src/main/res/values/styles.xml @@ -53,4 +53,10 @@      </style> +    <style name="blinkt.translucent" parent="blinkt.dialog"> +        <item name="android:windowNoTitle">true</item> +        <item name="android:windowActionBar">false</item> +        <item name="android:windowFullscreen">true</item> +        <item name="android:windowContentOverlay">@null</item> +    </style>  </resources>
\ No newline at end of file  | 
