strongswan/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnTileService.java

250 lines
6.5 KiB
Java

/*
* Copyright (C) 2018-2019 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
package org.strongswan.android.ui;
import android.annotation.TargetApi;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import org.strongswan.android.R;
import org.strongswan.android.data.VpnProfile;
import org.strongswan.android.data.VpnProfileDataSource;
import org.strongswan.android.data.VpnType;
import org.strongswan.android.logic.VpnStateService;
import org.strongswan.android.utils.Constants;
@TargetApi(Build.VERSION_CODES.N)
public class VpnTileService extends TileService implements VpnStateService.VpnStateListener
{
private boolean mListening;
private VpnProfileDataSource mDataSource;
private VpnStateService mService;
private final ServiceConnection mServiceConnection = new ServiceConnection()
{
@Override
public void onServiceDisconnected(ComponentName name)
{
mService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
mService = ((VpnStateService.LocalBinder)service).getService();
if (mListening && mDataSource != null)
{
mService.registerListener(VpnTileService.this);
updateTile();
}
}
};
@Override
public void onCreate()
{
super.onCreate();
Context context = getApplicationContext();
context.bindService(new Intent(context, VpnStateService.class),
mServiceConnection, Service.BIND_AUTO_CREATE);
mDataSource = new VpnProfileDataSource(this);
mDataSource.open();
}
@Override
public void onDestroy()
{
super.onDestroy();
if (mService != null)
{
getApplicationContext().unbindService(mServiceConnection);
}
mDataSource.close();
mDataSource = null;
}
@Override
public void onStartListening()
{
super.onStartListening();
mListening = true;
if (mService != null)
{
mService.registerListener(this);
updateTile();
}
}
@Override
public void onStopListening()
{
super.onStopListening();
mListening = false;
if (mService != null)
{
mService.unregisterListener(this);
}
}
private VpnProfile getProfile()
{
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
String uuid = pref.getString(Constants.PREF_DEFAULT_VPN_PROFILE, null);
if (uuid == null || uuid.equals(Constants.PREF_DEFAULT_VPN_PROFILE_MRU))
{
uuid = pref.getString(Constants.PREF_MRU_VPN_PROFILE, null);
}
return mDataSource != null ? mDataSource.getVpnProfile(uuid) : null;
}
@Override
public void onClick()
{
if (mService != null)
{
/* we operate on the current/most recently used profile, but fall back to configuration */
VpnProfile profile = mService.getProfile();
if (profile == null)
{
profile = getProfile();
}
else if (mDataSource != null)
{ /* always get the plain profile without cached password */
profile = mDataSource.getVpnProfile(profile.getId());
}
/* reconnect the profile in case of an error */
if (mService.getErrorState() == VpnStateService.ErrorState.NO_ERROR)
{
switch (mService.getState())
{
case CONNECTING:
case CONNECTED:
Runnable disconnect = new Runnable()
{
@Override
public void run()
{
mService.disconnect();
}
};
if (isLocked())
{
unlockAndRun(disconnect);
}
else
{
disconnect.run();
}
return;
}
}
if (profile != null)
{
Intent intent = new Intent(this, VpnProfileControlActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(VpnProfileControlActivity.START_PROFILE);
intent.putExtra(VpnProfileControlActivity.EXTRA_VPN_PROFILE_ID, profile.getUUID().toString());
if (profile.getVpnType().has(VpnType.VpnTypeFeature.USER_PASS) &&
profile.getPassword() == null)
{ /* the user will have to enter the password, so collapse the drawer */
startActivityAndCollapse(intent);
}
else
{
startActivity(intent);
}
return;
}
}
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityAndCollapse(intent);
}
@Override
public void stateChanged()
{
updateTile();
}
private void updateTile()
{
VpnProfile profile = mService.getProfile();
VpnStateService.State state = mService.getState();
VpnStateService.ErrorState error = mService.getErrorState();
/* same as above, only use the configured profile if we have no active profile */
if (profile == null)
{
profile = getProfile();
}
Tile tile = getQsTile();
if (tile == null)
{
return;
}
if (error != VpnStateService.ErrorState.NO_ERROR)
{
tile.setState(Tile.STATE_INACTIVE);
tile.setIcon(Icon.createWithResource(this, R.drawable.ic_notification_warning));
tile.setLabel(getString(R.string.tile_connect));
}
else
{
switch (state)
{
case DISCONNECTING:
case DISABLED:
tile.setState(Tile.STATE_INACTIVE);
tile.setIcon(Icon.createWithResource(this, R.drawable.ic_notification_disconnected));
tile.setLabel(getString(R.string.tile_connect));
break;
case CONNECTING:
tile.setState(Tile.STATE_ACTIVE);
tile.setIcon(Icon.createWithResource(this, R.drawable.ic_notification_connecting));
tile.setLabel(getString(R.string.tile_disconnect));
break;
case CONNECTED:
tile.setState(Tile.STATE_ACTIVE);
tile.setIcon(Icon.createWithResource(this, R.drawable.ic_notification));
tile.setLabel(getString(R.string.tile_disconnect));
break;
}
}
if (profile != null && !isSecure())
{
tile.setLabel(profile.getName());
}
tile.updateTile();
}
}