add aidl
This commit is contained in:
@@ -70,17 +70,22 @@
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".ShadowsocksService"
|
||||
android:name=".ShadowsocksNatService"
|
||||
android:process=":proxy"
|
||||
android:exported="false"/>
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.github.shadowsocks.ShadowsocksNatService"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".ShadowVpnService"
|
||||
android:name=".ShadowsocksVpnService"
|
||||
android:label="@string/app_name"
|
||||
android:process=":vpn"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.github.shadowsocks.ShadowsocksVpnService"/>
|
||||
<action android:name="android.net.VpnService"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.github.shadowsocks.aidl;
|
||||
|
||||
parcelable Config;
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.github.shadowsocks.aidl;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class Config implements Parcelable {
|
||||
|
||||
public boolean isGlobalProxy = true;
|
||||
public boolean isGFWList = true;
|
||||
public boolean isBypassApps = false;
|
||||
public boolean isTrafficStat = false;
|
||||
|
||||
public String profileName = "Untitled";
|
||||
public String proxy = "127.0.0.1";
|
||||
public String sitekey = "null";
|
||||
|
||||
public String encMethod = "rc4";
|
||||
public String proxiedAppString = "";
|
||||
|
||||
public int remotePort = 1984;
|
||||
public int localPort = 1080;
|
||||
|
||||
public static final Parcelable.Creator<Config> CREATOR = new
|
||||
Parcelable.Creator<Config>() {
|
||||
public Config createFromParcel(Parcel in) {
|
||||
return new Config(in);
|
||||
}
|
||||
|
||||
public Config[] newArray(int size) {
|
||||
return new Config[size];
|
||||
}
|
||||
};
|
||||
|
||||
public Config(boolean isGlobalProxy, boolean isGFWList, boolean isBypassApps,
|
||||
boolean isTrafficStat, String profileName, String proxy, String sitekey, String encMethod,
|
||||
String proxiedAppString, int remotePort, int localPort) {
|
||||
this.isGlobalProxy = isGlobalProxy;
|
||||
this.isGFWList = isGFWList;
|
||||
this.isBypassApps = isBypassApps;
|
||||
this.isTrafficStat = isTrafficStat;
|
||||
this.profileName = profileName;
|
||||
this.proxy = proxy;
|
||||
this.sitekey = sitekey;
|
||||
this.encMethod = encMethod;
|
||||
this.proxiedAppString = proxiedAppString;
|
||||
this.remotePort = remotePort;
|
||||
this.localPort = localPort;
|
||||
}
|
||||
|
||||
private Config(Parcel in) {
|
||||
readFromParcel(in);
|
||||
}
|
||||
|
||||
public void readFromParcel(Parcel in) {
|
||||
isGlobalProxy = in.readInt() == 1;
|
||||
isGFWList = in.readInt() == 1;
|
||||
isBypassApps = in.readInt() == 1;
|
||||
isTrafficStat = in.readInt() == 1;
|
||||
profileName = in.readString();
|
||||
proxy = in.readString();
|
||||
sitekey = in.readString();
|
||||
encMethod = in.readString();
|
||||
proxiedAppString = in.readString();
|
||||
remotePort = in.readInt();
|
||||
localPort = in.readInt();
|
||||
}
|
||||
|
||||
@Override public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeInt(isGlobalProxy ? 1: 0);
|
||||
out.writeInt(isGFWList ? 1: 0);
|
||||
out.writeInt(isBypassApps ? 1: 0);
|
||||
out.writeInt(isTrafficStat ? 1: 0);
|
||||
out.writeString(profileName);
|
||||
out.writeString(proxy);
|
||||
out.writeString(sitekey);
|
||||
out.writeString(encMethod);
|
||||
out.writeString(proxiedAppString);
|
||||
out.writeInt(remotePort);
|
||||
out.writeInt(localPort);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.github.shadowsocks.aidl;
|
||||
|
||||
import com.github.shadowsocks.aidl.Config;
|
||||
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback;
|
||||
|
||||
interface IShadowsocksService {
|
||||
int getMode();
|
||||
int getState();
|
||||
void start(in Config config);
|
||||
void stop();
|
||||
void registerCallback(IShadowsocksServiceCallback cb);
|
||||
void unregisterCallback(IShadowsocksServiceCallback cb);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.github.shadowsocks.aidl;
|
||||
|
||||
interface IShadowsocksServiceCallback {
|
||||
void stateChanged(int state, String msg);
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
<org.jraf.android.backport.switchwidget.Switch
|
||||
android:id="@+id/switchButton"
|
||||
android:enabled="false"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.github.shadowsocks
|
||||
|
||||
import android.os.RemoteCallbackList
|
||||
import com.github.shadowsocks.aidl.{Config, IShadowsocksService, IShadowsocksServiceCallback}
|
||||
import com.github.shadowsocks.utils.{Path, State}
|
||||
import java.io.{IOException, FileNotFoundException, FileReader, BufferedReader}
|
||||
import android.util.Log
|
||||
import android.app.Notification
|
||||
|
||||
trait BaseService {
|
||||
|
||||
var state = State.INIT
|
||||
|
||||
final val callbacks = new RemoteCallbackList[IShadowsocksServiceCallback]
|
||||
|
||||
protected val binder = new IShadowsocksService.Stub {
|
||||
override def getMode: Int = {
|
||||
getServiceMode
|
||||
}
|
||||
|
||||
override def getState: Int = {
|
||||
state
|
||||
}
|
||||
|
||||
override def unregisterCallback(cb: IShadowsocksServiceCallback) {
|
||||
if (cb != null ) callbacks.unregister(cb)
|
||||
}
|
||||
|
||||
override def registerCallback(cb: IShadowsocksServiceCallback) {
|
||||
if (cb != null) callbacks.register(cb)
|
||||
}
|
||||
|
||||
override def stop() {
|
||||
stopRunner()
|
||||
}
|
||||
|
||||
override def start(config: Config) {
|
||||
startRunner(config)
|
||||
}
|
||||
}
|
||||
|
||||
def startRunner(config: Config)
|
||||
def stopRunner()
|
||||
def getServiceMode: Int
|
||||
def getTag: String
|
||||
|
||||
def changeState(s: Int) {
|
||||
changeState(s, null)
|
||||
}
|
||||
|
||||
protected def changeState(s: Int, msg: String) {
|
||||
if (state != s) {
|
||||
val n = callbacks.beginBroadcast()
|
||||
for (i <- 0 to n -1) {
|
||||
callbacks.getBroadcastItem(i).stateChanged(s, msg)
|
||||
}
|
||||
callbacks.finishBroadcast()
|
||||
state = s
|
||||
}
|
||||
}
|
||||
|
||||
def getPid(name: String): Int = {
|
||||
try {
|
||||
val reader: BufferedReader = new BufferedReader(new FileReader(Path.BASE + name + ".pid"))
|
||||
val line = reader.readLine
|
||||
return Integer.valueOf(line)
|
||||
} catch {
|
||||
case e: FileNotFoundException =>
|
||||
Log.e(getTag, "Cannot open pid file: " + name)
|
||||
case e: IOException =>
|
||||
Log.e(getTag, "Cannot read pid file: " + name)
|
||||
case e: NumberFormatException =>
|
||||
Log.e(getTag, "Invalid pid", e)
|
||||
}
|
||||
-1
|
||||
}
|
||||
|
||||
def initSoundVibrateLights(notification: Notification) {
|
||||
notification.sound = null
|
||||
}
|
||||
}
|
||||
@@ -40,16 +40,28 @@
|
||||
package com.github.shadowsocks
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.os.{IBinder, Bundle}
|
||||
import android.net.VpnService
|
||||
import android.content.Intent
|
||||
import android.content.{ComponentName, ServiceConnection, Intent}
|
||||
import android.util.Log
|
||||
import android.preference.PreferenceManager
|
||||
import com.github.shadowsocks.utils.Extra
|
||||
import com.actionbarsherlock.app.SherlockActivity
|
||||
import com.github.shadowsocks.utils._
|
||||
import com.github.shadowsocks.aidl.IShadowsocksService
|
||||
|
||||
class ShadowVpnActivity extends Activity {
|
||||
|
||||
// Services
|
||||
var bgService: IShadowsocksService = null
|
||||
val connection = new ServiceConnection {
|
||||
override def onServiceConnected(name: ComponentName, service: IBinder) {
|
||||
bgService = IShadowsocksService.Stub.asInterface(service)
|
||||
}
|
||||
|
||||
override def onServiceDisconnected(name: ComponentName) {
|
||||
bgService = null
|
||||
}
|
||||
}
|
||||
|
||||
override def onCreate(savedInstanceState: Bundle) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val intent = VpnService.prepare(this)
|
||||
@@ -62,14 +74,12 @@ class ShadowVpnActivity extends Activity {
|
||||
|
||||
override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
|
||||
resultCode match {
|
||||
case Activity.RESULT_OK => {
|
||||
val intent: Intent = new Intent(this, classOf[ShadowVpnService])
|
||||
Extra.put(PreferenceManager.getDefaultSharedPreferences(this), intent)
|
||||
startService(intent)
|
||||
}
|
||||
case _ => {
|
||||
case Activity.RESULT_OK =>
|
||||
if (bgService != null) {
|
||||
bgService.start(ConfigUtils.load(PreferenceManager.getDefaultSharedPreferences(this)))
|
||||
}
|
||||
case _ =>
|
||||
Log.e(Shadowsocks.TAG, "Failed to start VpnService")
|
||||
}
|
||||
}
|
||||
finish()
|
||||
}
|
||||
|
||||
@@ -67,12 +67,11 @@ import scala.collection.mutable.ListBuffer
|
||||
import com.github.shadowsocks.database.Profile
|
||||
import com.nostra13.universalimageloader.core.download.BaseImageDownloader
|
||||
import com.github.shadowsocks.preferences.{ProfileEditTextPreference, PasswordEditTextPreference, SummaryEditTextPreference}
|
||||
import com.github.shadowsocks.database.Item
|
||||
import com.github.shadowsocks.database.Category
|
||||
import com.github.shadowsocks.utils._
|
||||
import com.github.shadowsocks.database.Item
|
||||
import com.github.shadowsocks.database.Category
|
||||
import com.google.zxing.integration.android.IntentIntegrator
|
||||
import com.github.shadowsocks.aidl.{IShadowsocksServiceCallback, IShadowsocksService}
|
||||
|
||||
class ProfileIconDownloader(context: Context, connectTimeout: Int, readTimeout: Int)
|
||||
extends BaseImageDownloader(context, connectTimeout, readTimeout) {
|
||||
@@ -103,10 +102,9 @@ object Typefaces {
|
||||
val t: Typeface = Typeface.createFromAsset(c.getAssets, assetPath)
|
||||
cache.put(assetPath, t)
|
||||
} catch {
|
||||
case e: Exception => {
|
||||
case e: Exception =>
|
||||
Log.e(TAG, "Could not get typeface '" + assetPath + "' because " + e.getMessage)
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
return cache.get(assetPath)
|
||||
@@ -131,8 +129,7 @@ object Shadowsocks {
|
||||
// Flags
|
||||
var vpnEnabled = -1
|
||||
|
||||
// Help functions
|
||||
|
||||
// Helper functions
|
||||
def updateListPreference(pref: Preference, value: String) {
|
||||
pref.setSummary(value)
|
||||
pref.asInstanceOf[ListPreference].setValue(value)
|
||||
@@ -172,10 +169,6 @@ object Shadowsocks {
|
||||
}
|
||||
}
|
||||
|
||||
def isServiceStarted(context: Context): Boolean = {
|
||||
ShadowsocksService.isServiceStarted(context) || ShadowVpnService.isServiceStarted(context)
|
||||
}
|
||||
|
||||
class ProxyFragment extends UnifiedPreferenceFragment {
|
||||
|
||||
var receiver: BroadcastReceiver = null
|
||||
@@ -194,8 +187,8 @@ object Shadowsocks {
|
||||
private def updatePreferenceScreen() {
|
||||
for (name <- Shadowsocks.PROXY_PREFS) {
|
||||
val pref = findPreference(name)
|
||||
Shadowsocks.updatePreference(pref, name,
|
||||
getActivity.asInstanceOf[Shadowsocks].currentProfile)
|
||||
Shadowsocks
|
||||
.updatePreference(pref, name, getActivity.asInstanceOf[Shadowsocks].currentProfile)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,8 +245,8 @@ object Shadowsocks {
|
||||
private def updatePreferenceScreen() {
|
||||
for (name <- Shadowsocks.FEATRUE_PREFS) {
|
||||
val pref = findPreference(name)
|
||||
Shadowsocks.updatePreference(pref, name,
|
||||
getActivity.asInstanceOf[Shadowsocks].currentProfile)
|
||||
Shadowsocks
|
||||
.updatePreference(pref, name, getActivity.asInstanceOf[Shadowsocks].currentProfile)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,9 +297,61 @@ class Shadowsocks
|
||||
var prepared = false
|
||||
var currentProfile = new Profile
|
||||
|
||||
// Services
|
||||
var currentServiceName = classOf[ShadowsocksNatService].getName
|
||||
var bgService: IShadowsocksService = null
|
||||
val callback = new IShadowsocksServiceCallback.Stub {
|
||||
override def stateChanged(state: Int, msg: String) {
|
||||
onStateChanged(state, msg)
|
||||
}
|
||||
}
|
||||
val connection = new ServiceConnection {
|
||||
override def onServiceConnected(name: ComponentName, service: IBinder) {
|
||||
// Initialize the background service
|
||||
bgService = IShadowsocksService.Stub.asInterface(service)
|
||||
try {
|
||||
bgService.registerCallback(callback)
|
||||
} catch {
|
||||
case ignored: RemoteException => // Nothing
|
||||
}
|
||||
// Update the UI
|
||||
switchButton.setEnabled(true)
|
||||
if (State.isAvailable(bgService.getState)) {
|
||||
if (status.getBoolean(Key.isRunning, false)) {
|
||||
spawn {
|
||||
crash_recovery()
|
||||
handler.sendEmptyMessage(MSG_CRASH_RECOVER)
|
||||
}
|
||||
}
|
||||
Crouton.cancelAllCroutons()
|
||||
onStateChanged(State.STOPPED, null)
|
||||
setPreferenceEnabled(enabled = true)
|
||||
} else {
|
||||
if (bgService.getMode == Mode.VPN) {
|
||||
val style = new Style.Builder().setBackgroundColorValue(Style.holoBlueLight).build()
|
||||
val config = new Configuration.Builder().setDuration(Configuration.DURATION_LONG).build()
|
||||
Crouton
|
||||
.makeText(Shadowsocks.this, R.string.vpn_status, style)
|
||||
.setConfiguration(config)
|
||||
.show()
|
||||
switchButton.setEnabled(false)
|
||||
}
|
||||
switchButton.setChecked(true)
|
||||
onStateChanged(State.CONNECTED, null)
|
||||
setPreferenceEnabled(enabled = false)
|
||||
}
|
||||
// set the listener
|
||||
switchButton.setOnCheckedChangeListener(Shadowsocks.this)
|
||||
}
|
||||
|
||||
override def onServiceDisconnected(name: ComponentName) {
|
||||
switchButton.setEnabled(false)
|
||||
bgService = null
|
||||
}
|
||||
}
|
||||
|
||||
lazy val settings = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
lazy val status = getSharedPreferences(Key.status, Context.MODE_PRIVATE)
|
||||
lazy val stateReceiver = new StateBroadcastReceiver
|
||||
lazy val preferenceReceiver = new PreferenceBroadcastReceiver
|
||||
lazy val drawer = MenuDrawer.attach(this)
|
||||
lazy val menuAdapter = new MenuAdapter(this, getMenuList)
|
||||
@@ -341,9 +386,8 @@ class Shadowsocks
|
||||
try {
|
||||
files = assetManager.list(path)
|
||||
} catch {
|
||||
case e: IOException => {
|
||||
case e: IOException =>
|
||||
Log.e(Shadowsocks.TAG, e.getMessage)
|
||||
}
|
||||
}
|
||||
if (files != null) {
|
||||
for (file <- files) {
|
||||
@@ -363,9 +407,8 @@ class Shadowsocks
|
||||
out.close()
|
||||
out = null
|
||||
} catch {
|
||||
case ex: Exception => {
|
||||
case ex: Exception =>
|
||||
Log.e(Shadowsocks.TAG, ex.getMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -409,9 +452,8 @@ class Shadowsocks
|
||||
val pi: PackageInfo = getPackageManager.getPackageInfo(getPackageName, 0)
|
||||
version = pi.versionName
|
||||
} catch {
|
||||
case e: PackageManager.NameNotFoundException => {
|
||||
case e: PackageManager.NameNotFoundException =>
|
||||
version = "Package name not found"
|
||||
}
|
||||
}
|
||||
version
|
||||
}
|
||||
@@ -445,12 +487,10 @@ class Shadowsocks
|
||||
def onCheckedChanged(compoundButton: CompoundButton, checked: Boolean) {
|
||||
if (compoundButton eq switchButton) {
|
||||
checked match {
|
||||
case true => {
|
||||
case true =>
|
||||
prepareStartService()
|
||||
}
|
||||
case false => {
|
||||
case false =>
|
||||
serviceStop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -494,15 +534,26 @@ class Shadowsocks
|
||||
/** Called when the activity is first created. */
|
||||
override def onCreate(savedInstanceState: Bundle) {
|
||||
|
||||
// Initialize preference
|
||||
// Initialize the preference
|
||||
setHeaderRes(R.xml.shadowsocks_headers)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Initialize profile
|
||||
// Initialize the profile
|
||||
currentProfile = {
|
||||
profileManager.getProfile(settings.getInt(Key.profileId, -1)) getOrElse currentProfile
|
||||
}
|
||||
|
||||
// Update the profile
|
||||
if (!status.getBoolean(getVersionName, false)) {
|
||||
val h = showProgress(getString(R.string.initializing))
|
||||
status.edit.putBoolean(getVersionName, true).apply()
|
||||
spawn {
|
||||
reset()
|
||||
currentProfile = profileManager.create()
|
||||
h.sendEmptyMessage(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize drawer
|
||||
menuAdapter.setActiveId(settings.getInt(Key.profileId, -1))
|
||||
menuAdapter.setListener(this)
|
||||
@@ -527,23 +578,19 @@ class Shadowsocks
|
||||
getSupportActionBar.setIcon(R.drawable.ic_stat_shadowsocks)
|
||||
|
||||
// Register broadcast receiver
|
||||
registerReceiver(stateReceiver, new IntentFilter(Action.UPDATE_STATE))
|
||||
registerReceiver(preferenceReceiver, new IntentFilter(Action.UPDATE_PREFS))
|
||||
|
||||
// Update status
|
||||
if (!Shadowsocks.isServiceStarted(this)) {
|
||||
spawn {
|
||||
status.edit.putBoolean(Key.isRoot, Utils.getRoot).apply()
|
||||
}
|
||||
if (!status.getBoolean(getVersionName, false)) {
|
||||
val h = showProgress(getString(R.string.initializing))
|
||||
status.edit.putBoolean(getVersionName, true).apply()
|
||||
spawn {
|
||||
reset()
|
||||
currentProfile = profileManager.create()
|
||||
h.sendEmptyMessage(0)
|
||||
// Bind to the service
|
||||
spawn {
|
||||
status.edit.putBoolean(Key.isRoot, Utils.getRoot).apply()
|
||||
handler.post(new Runnable {
|
||||
override def run() {
|
||||
val isRoot = status.getBoolean(Key.isRoot, false)
|
||||
val s = if (isRoot) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService]
|
||||
bindService(new Intent(s.getName), connection, Context.BIND_AUTO_CREATE)
|
||||
startService(new Intent(Shadowsocks.this, s))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -571,15 +618,15 @@ class Shadowsocks
|
||||
drawer.setActiveView(v, pos)
|
||||
}
|
||||
|
||||
|
||||
def newProfile(id: Int) {
|
||||
|
||||
val builder = new AlertDialog.Builder(this)
|
||||
builder.setTitle(R.string.add_profile)
|
||||
.setItems(R.array.add_profile_methods, new DialogInterface.OnClickListener() {
|
||||
builder
|
||||
.setTitle(R.string.add_profile)
|
||||
.setItems(R.array.add_profile_methods, new DialogInterface.OnClickListener() {
|
||||
def onClick(dialog: DialogInterface, which: Int) {
|
||||
which match {
|
||||
case 0 => {
|
||||
case 0 =>
|
||||
dialog.dismiss()
|
||||
val h = showProgress(getString(R.string.loading))
|
||||
h.postDelayed(new Runnable() {
|
||||
@@ -589,17 +636,14 @@ class Shadowsocks
|
||||
h.sendEmptyMessage(0)
|
||||
}
|
||||
}, 600)
|
||||
}
|
||||
case 1 => {
|
||||
case 1 =>
|
||||
dialog.dismiss()
|
||||
addProfile(id)
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
})
|
||||
builder.create().show()
|
||||
|
||||
}
|
||||
|
||||
def addProfile(profile: Profile) {
|
||||
@@ -737,7 +781,7 @@ class Shadowsocks
|
||||
EasyTracker
|
||||
.getInstance(this)
|
||||
.send(
|
||||
MapBuilder.createEvent(Shadowsocks.TAG, "flush_dnscache", getVersionName, null).build())
|
||||
MapBuilder.createEvent(Shadowsocks.TAG, "flush_dnscache", getVersionName, null).build())
|
||||
flushDnsCache()
|
||||
})
|
||||
|
||||
@@ -753,10 +797,9 @@ class Shadowsocks
|
||||
|
||||
override def onOptionsItemSelected(item: com.actionbarsherlock.view.MenuItem): Boolean = {
|
||||
item.getItemId match {
|
||||
case android.R.id.home => {
|
||||
case android.R.id.home =>
|
||||
drawer.toggleMenu()
|
||||
return true
|
||||
}
|
||||
}
|
||||
super.onOptionsItemSelected(item)
|
||||
}
|
||||
@@ -768,38 +811,7 @@ class Shadowsocks
|
||||
|
||||
protected override def onResume() {
|
||||
super.onResume()
|
||||
if (!prepared) {
|
||||
if (Shadowsocks.isServiceStarted(this)) {
|
||||
switchButton.setChecked(true)
|
||||
if (ShadowVpnService.isServiceStarted(this)) {
|
||||
val style = new Style.Builder().setBackgroundColorValue(Style.holoBlueLight).build()
|
||||
val config = new Configuration.Builder().setDuration(Configuration.DURATION_LONG).build()
|
||||
switchButton.setEnabled(false)
|
||||
Crouton
|
||||
.makeText(Shadowsocks.this, R.string.vpn_status, style)
|
||||
.setConfiguration(config)
|
||||
.show()
|
||||
}
|
||||
setPreferenceEnabled(enabled = false)
|
||||
onStateChanged(State.CONNECTED, null)
|
||||
} else {
|
||||
switchButton.setEnabled(true)
|
||||
switchButton.setChecked(false)
|
||||
Crouton.cancelAllCroutons()
|
||||
setPreferenceEnabled(enabled = true)
|
||||
if (status.getBoolean(Key.isRunning, false)) {
|
||||
spawn {
|
||||
crash_recovery()
|
||||
handler.sendEmptyMessage(MSG_CRASH_RECOVER)
|
||||
}
|
||||
}
|
||||
onStateChanged(State.STOPPED, null)
|
||||
}
|
||||
}
|
||||
|
||||
switchButton.setOnCheckedChangeListener(this)
|
||||
|
||||
Config.refresh(this)
|
||||
ConfigUtils.refresh(this)
|
||||
}
|
||||
|
||||
private def setPreferenceEnabled(enabled: Boolean) {
|
||||
@@ -848,8 +860,11 @@ class Shadowsocks
|
||||
|
||||
override def onDestroy() {
|
||||
super.onDestroy()
|
||||
if (bgService != null) {
|
||||
bgService.unregisterCallback(callback)
|
||||
unbindService(connection)
|
||||
}
|
||||
Crouton.cancelAllCroutons()
|
||||
unregisterReceiver(stateReceiver)
|
||||
unregisterReceiver(preferenceReceiver)
|
||||
new BackupManager(this).dataChanged()
|
||||
}
|
||||
@@ -891,16 +906,14 @@ class Shadowsocks
|
||||
}
|
||||
} else {
|
||||
resultCode match {
|
||||
case Activity.RESULT_OK => {
|
||||
case Activity.RESULT_OK =>
|
||||
prepared = true
|
||||
if (!serviceStart) {
|
||||
switchButton.setChecked(false)
|
||||
}
|
||||
}
|
||||
case _ => {
|
||||
case _ =>
|
||||
clearDialog()
|
||||
Log.e(Shadowsocks.TAG, "Failed to start VpnService")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -918,7 +931,7 @@ class Shadowsocks
|
||||
}
|
||||
|
||||
def serviceStop() {
|
||||
sendBroadcast(new Intent(Action.CLOSE))
|
||||
if (bgService != null) bgService.stop()
|
||||
}
|
||||
|
||||
/** Called when connect button is clicked. */
|
||||
@@ -935,26 +948,20 @@ class Shadowsocks
|
||||
return false
|
||||
}
|
||||
} catch {
|
||||
case ex: Exception => {
|
||||
case ex: Exception =>
|
||||
this.showDialog(getString(R.string.port_alert))
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (bgService == null) return false
|
||||
|
||||
bgService.start(ConfigUtils.load(settings))
|
||||
|
||||
if (isVpnEnabled) {
|
||||
if (ShadowVpnService.isServiceStarted(this)) return false
|
||||
val intent: Intent = new Intent(this, classOf[ShadowVpnService])
|
||||
Extra.put(settings, intent)
|
||||
startService(intent)
|
||||
val style = new Style.Builder().setBackgroundColorValue(Style.holoBlueLight).build()
|
||||
val config = new Configuration.Builder().setDuration(Configuration.DURATION_LONG).build()
|
||||
Crouton.makeText(Shadowsocks.this, R.string.vpn_status, style).setConfiguration(config).show()
|
||||
switchButton.setEnabled(false)
|
||||
} else {
|
||||
if (ShadowsocksService.isServiceStarted(this)) return false
|
||||
val intent: Intent = new Intent(this, classOf[ShadowsocksService])
|
||||
Extra.put(settings, intent)
|
||||
startService(intent)
|
||||
}
|
||||
true
|
||||
}
|
||||
@@ -974,9 +981,8 @@ class Shadowsocks
|
||||
try {
|
||||
versionName = getPackageManager.getPackageInfo(getPackageName, 0).versionName
|
||||
} catch {
|
||||
case ex: PackageManager.NameNotFoundException => {
|
||||
case ex: PackageManager.NameNotFoundException =>
|
||||
versionName = ""
|
||||
}
|
||||
}
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
@@ -1014,44 +1020,45 @@ class Shadowsocks
|
||||
}
|
||||
|
||||
def onStateChanged(s: Int, m: String) {
|
||||
if (state != s) {
|
||||
state = s
|
||||
state match {
|
||||
case State.CONNECTING => {
|
||||
if (progressDialog == null) {
|
||||
progressDialog = ProgressDialog
|
||||
.show(Shadowsocks.this, "", getString(R.string.connecting), true, true)
|
||||
handler.post(new Runnable {
|
||||
override def run() {
|
||||
if (state != s) {
|
||||
state = s
|
||||
state match {
|
||||
case State.CONNECTING =>
|
||||
if (progressDialog == null) {
|
||||
progressDialog = ProgressDialog
|
||||
.show(Shadowsocks.this, "", getString(R.string.connecting), true, true)
|
||||
}
|
||||
setPreferenceEnabled(enabled = false)
|
||||
case State.CONNECTED =>
|
||||
clearDialog()
|
||||
if (!switchButton.isChecked) switchButton.setChecked(true)
|
||||
setPreferenceEnabled(enabled = false)
|
||||
case State.STOPPED =>
|
||||
clearDialog()
|
||||
if (switchButton.isChecked) {
|
||||
switchButton.setEnabled(true)
|
||||
switchButton.setChecked(false)
|
||||
Crouton.cancelAllCroutons()
|
||||
}
|
||||
if (m != null) {
|
||||
Crouton.cancelAllCroutons()
|
||||
val style = new Style.Builder().setBackgroundColorValue(Style.holoRedLight).build()
|
||||
val config = new Configuration.Builder()
|
||||
.setDuration(Configuration.DURATION_LONG)
|
||||
.build()
|
||||
Crouton
|
||||
.makeText(Shadowsocks.this, getString(R.string.vpn_error).format(m), style)
|
||||
.setConfiguration(config)
|
||||
.show()
|
||||
}
|
||||
setPreferenceEnabled(enabled = true)
|
||||
}
|
||||
setPreferenceEnabled(enabled = false)
|
||||
}
|
||||
case State.CONNECTED => {
|
||||
clearDialog()
|
||||
if (!switchButton.isChecked) switchButton.setChecked(true)
|
||||
setPreferenceEnabled(enabled = false)
|
||||
}
|
||||
case State.STOPPED => {
|
||||
clearDialog()
|
||||
if (switchButton.isChecked) {
|
||||
switchButton.setEnabled(true)
|
||||
switchButton.setChecked(false)
|
||||
Crouton.cancelAllCroutons()
|
||||
}
|
||||
if (m != null) {
|
||||
Crouton.cancelAllCroutons()
|
||||
val style = new Style.Builder().setBackgroundColorValue(Style.holoRedLight).build()
|
||||
val config = new Configuration.Builder()
|
||||
.setDuration(Configuration.DURATION_LONG)
|
||||
.build()
|
||||
Crouton
|
||||
.makeText(Shadowsocks.this, getString(R.string.vpn_error).format(m), style)
|
||||
.setConfiguration(config)
|
||||
.show()
|
||||
}
|
||||
setPreferenceEnabled(enabled = true)
|
||||
if (!isSinglePane) sendBroadcast(new Intent(Action.UPDATE_FRAGMENT))
|
||||
}
|
||||
}
|
||||
if (!isSinglePane) sendBroadcast(new Intent(Action.UPDATE_FRAGMENT))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
class PreferenceBroadcastReceiver extends BroadcastReceiver {
|
||||
@@ -1061,12 +1068,4 @@ class Shadowsocks
|
||||
}
|
||||
}
|
||||
|
||||
class StateBroadcastReceiver extends BroadcastReceiver {
|
||||
override def onReceive(context: Context, intent: Intent) {
|
||||
val state = intent.getIntExtra(Extra.STATE, State.INIT)
|
||||
val message = intent.getStringExtra(Extra.MESSAGE)
|
||||
onStateChanged(state, message)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+161
-218
@@ -57,22 +57,16 @@ import org.apache.http.conn.util.InetAddressUtils
|
||||
import scala.collection._
|
||||
import java.util.{TimerTask, Timer}
|
||||
import android.net.TrafficStats
|
||||
import scala.concurrent.ops._
|
||||
import com.github.shadowsocks.utils._
|
||||
import scala.Some
|
||||
import android.graphics.Color
|
||||
import com.github.shadowsocks.aidl.Config
|
||||
|
||||
case class TrafficStat(tx: Long, rx: Long, timestamp: Long)
|
||||
|
||||
object ShadowsocksService {
|
||||
def isServiceStarted(context: Context): Boolean = {
|
||||
Utils.isServiceStarted("com.github.shadowsocks.ShadowsocksService", context)
|
||||
}
|
||||
}
|
||||
class ShadowsocksNatService extends Service with BaseService {
|
||||
|
||||
class ShadowsocksService extends Service {
|
||||
|
||||
val TAG = "ShadowsocksService"
|
||||
val TAG = "ShadowsocksNatService"
|
||||
|
||||
val CMD_IPTABLES_RETURN = " -t nat -A OUTPUT -p tcp -d 0.0.0.0 -j RETURN\n"
|
||||
val CMD_IPTABLES_REDIRECT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " + "-j REDIRECT --to 8123\n"
|
||||
@@ -80,12 +74,6 @@ class ShadowsocksService extends Service {
|
||||
"-j DNAT --to-destination 127.0.0.1:8123\n"
|
||||
val DNS_PORT = 8153
|
||||
|
||||
val MSG_CONNECT_FINISH = 1
|
||||
val MSG_CONNECT_SUCCESS = 2
|
||||
val MSG_CONNECT_FAIL = 3
|
||||
val MSG_STOP_SELF = 4
|
||||
val MSG_VPN_ERROR = 5
|
||||
|
||||
private val mStartForegroundSignature = Array[Class[_]](classOf[Int], classOf[Notification])
|
||||
private val mStopForegroundSignature = Array[Class[_]](classOf[Boolean])
|
||||
private val mSetForegroundSignature = Array[Class[_]](classOf[Boolean])
|
||||
@@ -104,56 +92,25 @@ class ShadowsocksService extends Service {
|
||||
private var mStartForegroundArgs = new Array[AnyRef](2)
|
||||
private var mStopForegroundArgs = new Array[AnyRef](1)
|
||||
|
||||
private var state = State.INIT
|
||||
private var last: TrafficStat = null
|
||||
private var lastTxRate = 0
|
||||
private var lastRxRate = 0
|
||||
private var timer: Timer = null
|
||||
private val TIMER_INTERVAL = 2
|
||||
|
||||
private def changeState(s: Int) {
|
||||
if (state != s) {
|
||||
state = s
|
||||
val intent = new Intent(Action.UPDATE_STATE)
|
||||
intent.putExtra(Extra.STATE, state)
|
||||
sendBroadcast(intent)
|
||||
}
|
||||
}
|
||||
|
||||
val handler: Handler = new Handler {
|
||||
override def handleMessage(msg: Message) {
|
||||
msg.what match {
|
||||
case MSG_CONNECT_SUCCESS =>
|
||||
case Msg.CONNECT_SUCCESS =>
|
||||
changeState(State.CONNECTED)
|
||||
case MSG_CONNECT_FAIL =>
|
||||
case Msg.CONNECT_FAIL =>
|
||||
changeState(State.STOPPED)
|
||||
case MSG_STOP_SELF =>
|
||||
stopSelf()
|
||||
case _ =>
|
||||
}
|
||||
super.handleMessage(msg)
|
||||
}
|
||||
}
|
||||
|
||||
def getPid(name: String): Int = {
|
||||
try {
|
||||
val reader: BufferedReader = new BufferedReader(new FileReader(Path.BASE + name + ".pid"))
|
||||
val line = reader.readLine
|
||||
return Integer.valueOf(line)
|
||||
} catch {
|
||||
case e: FileNotFoundException => {
|
||||
Log.e(TAG, "Cannot open pid file: " + name)
|
||||
}
|
||||
case e: IOException => {
|
||||
Log.e(TAG, "Cannot read pid file: " + name)
|
||||
}
|
||||
case e: NumberFormatException => {
|
||||
Log.e(TAG, "Invalid pid", e)
|
||||
}
|
||||
}
|
||||
-1
|
||||
}
|
||||
|
||||
def startShadowsocksDaemon() {
|
||||
val cmd: String = (Path.BASE +
|
||||
"shadowsocks -b 127.0.0.1 -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" -m \"%s\" -f " +
|
||||
@@ -165,8 +122,8 @@ class ShadowsocksService extends Service {
|
||||
|
||||
def startDnsDaemon() {
|
||||
val cmd: String = Path.BASE + "pdnsd -c " + Path.BASE + "pdnsd.conf"
|
||||
val conf: String = Config.PDNSD.format("127.0.0.1")
|
||||
Config.printToFile(new File(Path.BASE + "pdnsd.conf"))(p => {
|
||||
val conf: String = ConfigUtils.PDNSD.format("127.0.0.1")
|
||||
ConfigUtils.printToFile(new File(Path.BASE + "pdnsd.conf"))(p => {
|
||||
p.println(conf)
|
||||
})
|
||||
Utils.runCommand(cmd)
|
||||
@@ -178,101 +135,17 @@ class ShadowsocksService extends Service {
|
||||
val pi: PackageInfo = getPackageManager.getPackageInfo(getPackageName, 0)
|
||||
version = pi.versionName
|
||||
} catch {
|
||||
case e: PackageManager.NameNotFoundException => {
|
||||
case e: PackageManager.NameNotFoundException =>
|
||||
version = "Package name not found"
|
||||
}
|
||||
}
|
||||
version
|
||||
}
|
||||
|
||||
def handleCommand(intent: Intent) {
|
||||
if (intent == null) {
|
||||
stopSelf()
|
||||
return
|
||||
}
|
||||
|
||||
changeState(State.CONNECTING)
|
||||
|
||||
config = Extra.get(intent)
|
||||
|
||||
if (config.isTrafficStat) {
|
||||
// initialize timer
|
||||
val task = new TimerTask {
|
||||
def run() {
|
||||
val pm = getSystemService(Context.POWER_SERVICE).asInstanceOf[PowerManager]
|
||||
val now = new TrafficStat(TrafficStats.getUidTxBytes(uid),
|
||||
TrafficStats.getUidRxBytes(uid), java.lang.System.currentTimeMillis())
|
||||
val txRate = ((now.tx - last.tx) / 1024 / TIMER_INTERVAL).toInt
|
||||
val rxRate = ((now.rx - last.rx) / 1024 / TIMER_INTERVAL).toInt
|
||||
last = now
|
||||
if (lastTxRate == txRate && lastRxRate == rxRate) {
|
||||
return
|
||||
} else {
|
||||
lastTxRate = txRate
|
||||
lastRxRate = rxRate
|
||||
}
|
||||
if ((pm.isScreenOn && state == State.CONNECTED) || (txRate == 0 && rxRate == 0)) {
|
||||
notifyForegroundAlert(getString(R.string.forward_success),
|
||||
getString(R.string.service_status).format(math.max(txRate, rxRate)), math.max(txRate, rxRate))
|
||||
}
|
||||
}
|
||||
}
|
||||
last = new TrafficStat(TrafficStats.getUidTxBytes(uid),
|
||||
TrafficStats.getUidRxBytes(uid), java.lang.System.currentTimeMillis())
|
||||
timer = new Timer(true)
|
||||
timer.schedule(task, TIMER_INTERVAL * 1000, TIMER_INTERVAL * 1000)
|
||||
}
|
||||
|
||||
spawn {
|
||||
|
||||
if (config.proxy == "198.199.101.152") {
|
||||
val container = getApplication.asInstanceOf[ShadowsocksApplication].tagContainer
|
||||
try {
|
||||
config = Config.getPublicConfig(getBaseContext, container, config)
|
||||
} catch {
|
||||
case ex: Exception => {
|
||||
notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed))
|
||||
stopSelf()
|
||||
handler.sendEmptyMessageDelayed(MSG_CONNECT_FAIL, 500)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
killProcesses()
|
||||
|
||||
var resolved: Boolean = false
|
||||
if (!InetAddressUtils.isIPv4Address(config.proxy) &&
|
||||
!InetAddressUtils.isIPv6Address(config.proxy)) {
|
||||
Utils.resolve(config.proxy, enableIPv6 = true) match {
|
||||
case Some(a) =>
|
||||
config.proxy = a
|
||||
resolved = true
|
||||
case None => resolved = false
|
||||
}
|
||||
} else {
|
||||
resolved = true
|
||||
}
|
||||
|
||||
hasRedirectSupport = Utils.getHasRedirectSupport
|
||||
|
||||
if (resolved && handleConnection) {
|
||||
notifyForegroundAlert(getString(R.string.forward_success),
|
||||
getString(R.string.service_running).format(config.profileName))
|
||||
handler.sendEmptyMessageDelayed(MSG_CONNECT_SUCCESS, 500)
|
||||
} else {
|
||||
notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed))
|
||||
stopSelf()
|
||||
handler.sendEmptyMessageDelayed(MSG_CONNECT_FAIL, 500)
|
||||
}
|
||||
handler.sendEmptyMessageDelayed(MSG_CONNECT_FINISH, 500)
|
||||
}
|
||||
}
|
||||
|
||||
def startRedsocksDaemon() {
|
||||
val conf = Config.REDSOCKS.format(config.localPort)
|
||||
val cmd = "%sredsocks -p %sredsocks.pid -c %sredsocks.conf".format(Path.BASE, Path.BASE, Path.BASE)
|
||||
Config.printToFile(new File(Path.BASE + "redsocks.conf"))(p => {
|
||||
val conf = ConfigUtils.REDSOCKS.format(config.localPort)
|
||||
val cmd = "%sredsocks -p %sredsocks.pid -c %sredsocks.conf"
|
||||
.format(Path.BASE, Path.BASE, Path.BASE)
|
||||
ConfigUtils.printToFile(new File(Path.BASE + "redsocks.conf"))(p => {
|
||||
p.println(conf)
|
||||
})
|
||||
Utils.runRootCommand(cmd)
|
||||
@@ -291,14 +164,14 @@ class ShadowsocksService extends Service {
|
||||
try {
|
||||
t.join(300)
|
||||
} catch {
|
||||
case ignored: InterruptedException => {
|
||||
}
|
||||
case ignored: InterruptedException =>
|
||||
}
|
||||
!t.isAlive
|
||||
}
|
||||
|
||||
/** Called when the activity is first created. */
|
||||
def handleConnection: Boolean = {
|
||||
|
||||
startShadowsocksDaemon()
|
||||
startDnsDaemon()
|
||||
startRedsocksDaemon()
|
||||
@@ -308,21 +181,14 @@ class ShadowsocksService extends Service {
|
||||
true
|
||||
}
|
||||
|
||||
def initSoundVibrateLights(notification: Notification) {
|
||||
notification.sound = null
|
||||
notification.defaults |= Notification.DEFAULT_LIGHTS
|
||||
}
|
||||
|
||||
def invokeMethod(method: Method, args: Array[AnyRef]) {
|
||||
try {
|
||||
method.invoke(this, mStartForegroundArgs: _*)
|
||||
} catch {
|
||||
case e: InvocationTargetException => {
|
||||
case e: InvocationTargetException =>
|
||||
Log.w(TAG, "Unable to invoke method", e)
|
||||
}
|
||||
case e: IllegalAccessException => {
|
||||
case e: IllegalAccessException =>
|
||||
Log.w(TAG, "Unable to invoke method", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,8 +206,9 @@ class ShadowsocksService extends Service {
|
||||
|
||||
val icon = getResources.getDrawable(R.drawable.ic_stat_shadowsocks)
|
||||
if (rate >= 0) {
|
||||
val bitmap =Utils.getBitmap(rate.toString, icon.getIntrinsicWidth * 4,
|
||||
icon.getIntrinsicHeight * 4, Color.TRANSPARENT)
|
||||
val bitmap = Utils
|
||||
.getBitmap(rate.toString, icon.getIntrinsicWidth * 4, icon.getIntrinsicHeight * 4,
|
||||
Color.TRANSPARENT)
|
||||
builder.setLargeIcon(bitmap)
|
||||
|
||||
if (rate < 1000) {
|
||||
@@ -352,7 +219,6 @@ class ShadowsocksService extends Service {
|
||||
} else {
|
||||
builder.setSmallIcon(R.drawable.ic_stat_speed, 1091)
|
||||
}
|
||||
|
||||
} else {
|
||||
builder.setSmallIcon(R.drawable.ic_stat_shadowsocks)
|
||||
}
|
||||
@@ -364,7 +230,7 @@ class ShadowsocksService extends Service {
|
||||
.setContentText(info)
|
||||
.setContentIntent(contentIntent)
|
||||
.addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.stop),
|
||||
actionIntent)
|
||||
actionIntent)
|
||||
|
||||
startForegroundCompat(1, builder.build)
|
||||
}
|
||||
@@ -386,20 +252,17 @@ class ShadowsocksService extends Service {
|
||||
}
|
||||
|
||||
def onBind(intent: Intent): IBinder = {
|
||||
null
|
||||
if (classOf[ShadowsocksNatService].getName equals intent.getAction) {
|
||||
binder
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override def onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
Config.refresh(this)
|
||||
|
||||
EasyTracker
|
||||
.getInstance(this)
|
||||
.send(MapBuilder
|
||||
.createEvent(TAG, "start", getVersionName, 0L)
|
||||
.set(Fields.SESSION_CONTROL, "start")
|
||||
.build())
|
||||
ConfigUtils.refresh(this)
|
||||
|
||||
notificationManager = this
|
||||
.getSystemService(Context.NOTIFICATION_SERVICE)
|
||||
@@ -408,63 +271,19 @@ class ShadowsocksService extends Service {
|
||||
mStartForeground = getClass.getMethod("startForeground", mStartForegroundSignature: _*)
|
||||
mStopForeground = getClass.getMethod("stopForeground", mStopForegroundSignature: _*)
|
||||
} catch {
|
||||
case e: NoSuchMethodException => {
|
||||
case e: NoSuchMethodException =>
|
||||
mStartForeground = {
|
||||
mStopForeground = null
|
||||
mStopForeground
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
mSetForeground = getClass.getMethod("setForeground", mSetForegroundSignature: _*)
|
||||
} catch {
|
||||
case e: NoSuchMethodException => {
|
||||
case e: NoSuchMethodException =>
|
||||
throw new IllegalStateException(
|
||||
"OS doesn't have Service.startForeground OR Service.setForeground!")
|
||||
}
|
||||
}
|
||||
|
||||
// register close receiver
|
||||
val filter = new IntentFilter()
|
||||
filter.addAction(Intent.ACTION_SHUTDOWN)
|
||||
filter.addAction(Action.CLOSE)
|
||||
receiver = new BroadcastReceiver() {
|
||||
def onReceive(p1: Context, p2: Intent) {
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
registerReceiver(receiver, filter)
|
||||
}
|
||||
|
||||
/** Called when the activity is closed. */
|
||||
override def onDestroy() {
|
||||
|
||||
// reset timer
|
||||
if (timer != null) {
|
||||
timer.cancel()
|
||||
timer = null
|
||||
}
|
||||
|
||||
// clean up context
|
||||
changeState(State.STOPPED)
|
||||
|
||||
EasyTracker
|
||||
.getInstance(this)
|
||||
.send(MapBuilder
|
||||
.createEvent(TAG, "stop", getVersionName, 0L)
|
||||
.set(Fields.SESSION_CONTROL, "stop")
|
||||
.build())
|
||||
|
||||
stopForegroundCompat(1)
|
||||
if (receiver != null) {
|
||||
unregisterReceiver(receiver)
|
||||
receiver = null
|
||||
}
|
||||
|
||||
// reset NAT
|
||||
killProcesses()
|
||||
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
def killProcesses() {
|
||||
@@ -489,12 +308,7 @@ class ShadowsocksService extends Service {
|
||||
Utils.runCommand(sb.toString())
|
||||
}
|
||||
|
||||
override def onStart(intent: Intent, startId: Int) {
|
||||
handleCommand(intent)
|
||||
}
|
||||
|
||||
override def onStartCommand(intent: Intent, flags: Int, startId: Int): Int = {
|
||||
handleCommand(intent)
|
||||
Service.START_STICKY
|
||||
}
|
||||
|
||||
@@ -593,12 +407,10 @@ class ShadowsocksService extends Service {
|
||||
try {
|
||||
mStopForeground.invoke(this, mStopForegroundArgs: _*)
|
||||
} catch {
|
||||
case e: InvocationTargetException => {
|
||||
case e: InvocationTargetException =>
|
||||
Log.w(TAG, "Unable to invoke stopForeground", e)
|
||||
}
|
||||
case e: IllegalAccessException => {
|
||||
case e: IllegalAccessException =>
|
||||
Log.w(TAG, "Unable to invoke stopForeground", e)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -606,4 +418,135 @@ class ShadowsocksService extends Service {
|
||||
mSetForegroundArgs(0) = boolean2Boolean(x = false)
|
||||
invokeMethod(mSetForeground, mSetForegroundArgs)
|
||||
}
|
||||
|
||||
override def startRunner(c: Config) {
|
||||
|
||||
var config = c
|
||||
|
||||
// register close receiver
|
||||
val filter = new IntentFilter()
|
||||
filter.addAction(Intent.ACTION_SHUTDOWN)
|
||||
filter.addAction(Action.CLOSE)
|
||||
receiver = new BroadcastReceiver() {
|
||||
def onReceive(p1: Context, p2: Intent) {
|
||||
stopRunner()
|
||||
}
|
||||
}
|
||||
registerReceiver(receiver, filter)
|
||||
|
||||
// start tracker
|
||||
EasyTracker
|
||||
.getInstance(this)
|
||||
.send(MapBuilder
|
||||
.createEvent(TAG, "start", getVersionName, 0L)
|
||||
.set(Fields.SESSION_CONTROL, "start")
|
||||
.build())
|
||||
|
||||
changeState(State.CONNECTING)
|
||||
|
||||
if (config.isTrafficStat) {
|
||||
// initialize timer
|
||||
val task = new TimerTask {
|
||||
def run() {
|
||||
val pm = getSystemService(Context.POWER_SERVICE).asInstanceOf[PowerManager]
|
||||
val now = new
|
||||
TrafficStat(TrafficStats.getUidTxBytes(uid), TrafficStats.getUidRxBytes(uid),
|
||||
java.lang.System.currentTimeMillis())
|
||||
val txRate = ((now.tx - last.tx) / 1024 / TIMER_INTERVAL).toInt
|
||||
val rxRate = ((now.rx - last.rx) / 1024 / TIMER_INTERVAL).toInt
|
||||
last = now
|
||||
if (lastTxRate == txRate && lastRxRate == rxRate) {
|
||||
return
|
||||
} else {
|
||||
lastTxRate = txRate
|
||||
lastRxRate = rxRate
|
||||
}
|
||||
if ((pm.isScreenOn && state == State.CONNECTED) || (txRate == 0 && rxRate == 0)) {
|
||||
notifyForegroundAlert(getString(R.string.forward_success),
|
||||
getString(R.string.service_status).format(math.max(txRate, rxRate)),
|
||||
math.max(txRate, rxRate))
|
||||
}
|
||||
}
|
||||
}
|
||||
last = new TrafficStat(TrafficStats.getUidTxBytes(uid), TrafficStats.getUidRxBytes(uid),
|
||||
java.lang.System.currentTimeMillis())
|
||||
timer = new Timer(true)
|
||||
timer.schedule(task, TIMER_INTERVAL * 1000, TIMER_INTERVAL * 1000)
|
||||
}
|
||||
|
||||
if (config.proxy == "198.199.101.152") {
|
||||
val container = getApplication.asInstanceOf[ShadowsocksApplication].tagContainer
|
||||
try {
|
||||
config = ConfigUtils.getPublicConfig(getBaseContext, container, config)
|
||||
} catch {
|
||||
case ex: Exception =>
|
||||
notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed))
|
||||
stopRunner()
|
||||
handler.sendEmptyMessageDelayed(Msg.CONNECT_FAIL, 500)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
killProcesses()
|
||||
|
||||
var resolved: Boolean = false
|
||||
if (!InetAddressUtils.isIPv4Address(config.proxy) &&
|
||||
!InetAddressUtils.isIPv6Address(config.proxy)) {
|
||||
Utils.resolve(config.proxy, enableIPv6 = true) match {
|
||||
case Some(a) =>
|
||||
config.proxy = a
|
||||
resolved = true
|
||||
case None => resolved = false
|
||||
}
|
||||
} else {
|
||||
resolved = true
|
||||
}
|
||||
|
||||
hasRedirectSupport = Utils.getHasRedirectSupport
|
||||
|
||||
if (resolved && handleConnection) {
|
||||
notifyForegroundAlert(getString(R.string.forward_success),
|
||||
getString(R.string.service_running).format(config.profileName))
|
||||
handler.sendEmptyMessageDelayed(Msg.CONNECT_SUCCESS, 500)
|
||||
} else {
|
||||
notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed))
|
||||
stopRunner()
|
||||
handler.sendEmptyMessageDelayed(Msg.CONNECT_FAIL, 500)
|
||||
}
|
||||
handler.sendEmptyMessageDelayed(Msg.CONNECT_FINISH, 500)
|
||||
}
|
||||
|
||||
override def stopRunner() {
|
||||
|
||||
// change the state
|
||||
changeState(State.STOPPED)
|
||||
|
||||
// stop the tracker
|
||||
EasyTracker
|
||||
.getInstance(this)
|
||||
.send(MapBuilder
|
||||
.createEvent(TAG, "stop", getVersionName, 0L)
|
||||
.set(Fields.SESSION_CONTROL, "stop")
|
||||
.build())
|
||||
|
||||
// reset timer
|
||||
if (timer != null) {
|
||||
timer.cancel()
|
||||
timer = null
|
||||
}
|
||||
|
||||
// clean up context
|
||||
stopForegroundCompat(1)
|
||||
if (receiver != null) {
|
||||
unregisterReceiver(receiver)
|
||||
receiver = null
|
||||
}
|
||||
|
||||
// reset NAT
|
||||
killProcesses()
|
||||
}
|
||||
|
||||
override def getTag = TAG
|
||||
|
||||
override def getServiceMode = Mode.NAT
|
||||
}
|
||||
@@ -54,33 +54,17 @@ class ShadowsocksReceiver extends BroadcastReceiver {
|
||||
val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val status = context.getSharedPreferences(Key.status, Context.MODE_PRIVATE)
|
||||
|
||||
if (intent.getAction == Action.UPDATE_STATE) {
|
||||
val state = intent.getIntExtra(Extra.STATE, State.INIT)
|
||||
val running = state match {
|
||||
case State.CONNECTING => true
|
||||
case State.CONNECTED => true
|
||||
case _ => false
|
||||
}
|
||||
status.edit.putBoolean(Key.isRunning, running).commit()
|
||||
return
|
||||
}
|
||||
|
||||
var versionName: String = null
|
||||
try {
|
||||
versionName = context.getPackageManager.getPackageInfo(context.getPackageName, 0).versionName
|
||||
} catch {
|
||||
case e: PackageManager.NameNotFoundException => {
|
||||
case e: PackageManager.NameNotFoundException =>
|
||||
versionName = "NONE"
|
||||
}
|
||||
}
|
||||
val isAutoConnect: Boolean = settings.getBoolean(Key.isAutoConnect, false)
|
||||
val isInstalled: Boolean = status.getBoolean(versionName, false)
|
||||
if (isAutoConnect && isInstalled) {
|
||||
if (Utils.getRoot) {
|
||||
if (ShadowsocksService.isServiceStarted(context)) return
|
||||
val intent: Intent = new Intent(context, classOf[ShadowsocksService])
|
||||
Extra.put(settings, intent)
|
||||
context.startService(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+124
-185
@@ -51,27 +51,15 @@ import java.io._
|
||||
import android.net.VpnService
|
||||
import org.apache.http.conn.util.InetAddressUtils
|
||||
import android.os.Message
|
||||
import scala.concurrent.ops._
|
||||
import org.apache.commons.net.util.SubnetUtils
|
||||
import java.net.InetAddress
|
||||
import com.github.shadowsocks.utils._
|
||||
import scala.Some
|
||||
import com.github.shadowsocks.aidl.Config
|
||||
|
||||
object ShadowVpnService {
|
||||
def isServiceStarted(context: Context): Boolean = {
|
||||
Utils.isServiceStarted("com.github.shadowsocks.ShadowVpnService", context)
|
||||
}
|
||||
}
|
||||
class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
|
||||
class ShadowVpnService extends VpnService {
|
||||
|
||||
val TAG = "ShadowVpnService"
|
||||
|
||||
val MSG_CONNECT_FINISH = 1
|
||||
val MSG_CONNECT_SUCCESS = 2
|
||||
val MSG_CONNECT_FAIL = 3
|
||||
val MSG_STOP_SELF = 5
|
||||
val MSG_VPN_ERROR = 6
|
||||
val TAG = "ShadowsocksVpnService"
|
||||
|
||||
val VPN_MTU = 1500
|
||||
|
||||
@@ -83,60 +71,21 @@ class ShadowVpnService extends VpnService {
|
||||
var apps: Array[ProxiedApp] = null
|
||||
var config: Config = null
|
||||
|
||||
private var state = State.INIT
|
||||
private var message: String = null
|
||||
|
||||
def changeState(s: Int) {
|
||||
changeState(s, null)
|
||||
}
|
||||
|
||||
def changeState(s: Int, m: String) {
|
||||
if (state != s) {
|
||||
state = s
|
||||
if (m != null) message = m
|
||||
val intent = new Intent(Action.UPDATE_STATE)
|
||||
intent.putExtra(Extra.STATE, state)
|
||||
intent.putExtra(Extra.MESSAGE, message)
|
||||
sendBroadcast(intent)
|
||||
}
|
||||
}
|
||||
|
||||
val handler: Handler = new Handler {
|
||||
override def handleMessage(msg: Message) {
|
||||
msg.what match {
|
||||
case MSG_CONNECT_SUCCESS =>
|
||||
case Msg.CONNECT_SUCCESS =>
|
||||
changeState(State.CONNECTED)
|
||||
case MSG_CONNECT_FAIL =>
|
||||
case Msg.CONNECT_FAIL =>
|
||||
changeState(State.STOPPED)
|
||||
case MSG_VPN_ERROR =>
|
||||
case Msg.VPN_ERROR =>
|
||||
if (msg.obj != null) changeState(State.STOPPED, msg.obj.asInstanceOf[String])
|
||||
case MSG_STOP_SELF =>
|
||||
stopSelf()
|
||||
case _ =>
|
||||
}
|
||||
super.handleMessage(msg)
|
||||
}
|
||||
}
|
||||
|
||||
def getPid(name: String): Int = {
|
||||
try {
|
||||
val reader: BufferedReader = new BufferedReader(new FileReader(Path.BASE + name + ".pid"))
|
||||
val line = reader.readLine
|
||||
return Integer.valueOf(line)
|
||||
} catch {
|
||||
case e: FileNotFoundException => {
|
||||
Log.e(TAG, "Cannot open pid file: " + name)
|
||||
}
|
||||
case e: IOException => {
|
||||
Log.e(TAG, "Cannot read pid file: " + name)
|
||||
}
|
||||
case e: NumberFormatException => {
|
||||
Log.e(TAG, "Invalid pid", e)
|
||||
}
|
||||
}
|
||||
-1
|
||||
}
|
||||
|
||||
def startShadowsocksDaemon() {
|
||||
val cmd: String = (Path.BASE +
|
||||
"shadowsocks -b 127.0.0.1 -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" -m \"%s\" -f " +
|
||||
@@ -148,8 +97,8 @@ class ShadowVpnService extends VpnService {
|
||||
|
||||
def startDnsDaemon() {
|
||||
val cmd: String = Path.BASE + "pdnsd -c " + Path.BASE + "pdnsd.conf"
|
||||
val conf: String = Config.PDNSD.format("0.0.0.0")
|
||||
Config.printToFile(new File(Path.BASE + "pdnsd.conf"))(p => {
|
||||
val conf: String = ConfigUtils.PDNSD.format("0.0.0.0")
|
||||
ConfigUtils.printToFile(new File(Path.BASE + "pdnsd.conf"))(p => {
|
||||
p.println(conf)
|
||||
})
|
||||
Utils.runCommand(cmd)
|
||||
@@ -161,74 +110,12 @@ class ShadowVpnService extends VpnService {
|
||||
val pi: PackageInfo = getPackageManager.getPackageInfo(getPackageName, 0)
|
||||
version = pi.versionName
|
||||
} catch {
|
||||
case e: PackageManager.NameNotFoundException => {
|
||||
case e: PackageManager.NameNotFoundException =>
|
||||
version = "Package name not found"
|
||||
}
|
||||
}
|
||||
version
|
||||
}
|
||||
|
||||
def handleCommand(intent: Intent) {
|
||||
|
||||
if (intent == null) {
|
||||
stopSelf()
|
||||
return
|
||||
}
|
||||
|
||||
if (VpnService.prepare(this) != null) {
|
||||
val i = new Intent(this, classOf[ShadowVpnActivity])
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(i)
|
||||
stopSelf()
|
||||
return
|
||||
}
|
||||
|
||||
changeState(State.CONNECTING)
|
||||
|
||||
config = Extra.get(intent)
|
||||
|
||||
spawn {
|
||||
if (config.proxy == "198.199.101.152") {
|
||||
val container = getApplication.asInstanceOf[ShadowsocksApplication].tagContainer
|
||||
try {
|
||||
config = Config.getPublicConfig(getBaseContext, container, config)
|
||||
} catch {
|
||||
case ex: Exception => {
|
||||
notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed))
|
||||
stopSelf()
|
||||
handler.sendEmptyMessageDelayed(MSG_CONNECT_FAIL, 500)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
killProcesses()
|
||||
|
||||
// Resolve server address
|
||||
var resolved: Boolean = false
|
||||
if (!InetAddressUtils.isIPv4Address(config.proxy) &&
|
||||
!InetAddressUtils.isIPv6Address(config.proxy)) {
|
||||
Utils.resolve(config.proxy, enableIPv6 = true) match {
|
||||
case Some(addr) =>
|
||||
config.proxy = addr
|
||||
resolved = true
|
||||
case None => resolved = false
|
||||
}
|
||||
} else {
|
||||
resolved = true
|
||||
}
|
||||
|
||||
if (resolved && handleConnection) {
|
||||
handler.sendEmptyMessageDelayed(MSG_CONNECT_SUCCESS, 300)
|
||||
} else {
|
||||
notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed))
|
||||
handler.sendEmptyMessageDelayed(MSG_CONNECT_FAIL, 300)
|
||||
handler.sendEmptyMessageDelayed(MSG_STOP_SELF, 500)
|
||||
}
|
||||
handler.sendEmptyMessageDelayed(MSG_CONNECT_FINISH, 300)
|
||||
}
|
||||
}
|
||||
|
||||
def waitForProcess(name: String): Boolean = {
|
||||
val pid: Int = getPid(name)
|
||||
if (pid == -1) return false
|
||||
@@ -242,8 +129,7 @@ class ShadowVpnService extends VpnService {
|
||||
try {
|
||||
t.join(300)
|
||||
} catch {
|
||||
case ignored: InterruptedException => {
|
||||
}
|
||||
case ignored: InterruptedException =>
|
||||
}
|
||||
!t.isAlive
|
||||
}
|
||||
@@ -308,18 +194,17 @@ class ShadowVpnService extends VpnService {
|
||||
try {
|
||||
conn = builder.establish()
|
||||
} catch {
|
||||
case ex: IllegalStateException => {
|
||||
case ex: IllegalStateException =>
|
||||
val msg = new Message()
|
||||
msg.what = MSG_VPN_ERROR
|
||||
msg.what = Msg.VPN_ERROR
|
||||
msg.obj = ex.getMessage
|
||||
handler.sendMessage(msg)
|
||||
conn = null
|
||||
}
|
||||
case ex: Exception => conn = null
|
||||
}
|
||||
|
||||
if (conn == null) {
|
||||
stopSelf()
|
||||
stopRunner()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -335,7 +220,7 @@ class ShadowVpnService extends VpnService {
|
||||
+ "--loglevel 3 "
|
||||
+ "--pid %stun2socks.pid")
|
||||
.format(PRIVATE_VLAN.format("2"), PRIVATE_VLAN.format("1"), config.localPort, fd, VPN_MTU,
|
||||
Path.BASE)
|
||||
Path.BASE)
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
|
||||
System.exec(cmd)
|
||||
}
|
||||
@@ -348,11 +233,6 @@ class ShadowVpnService extends VpnService {
|
||||
true
|
||||
}
|
||||
|
||||
def initSoundVibrateLights(notification: Notification) {
|
||||
notification.sound = null
|
||||
notification.defaults |= Notification.DEFAULT_LIGHTS
|
||||
}
|
||||
|
||||
def notifyAlert(title: String, info: String) {
|
||||
val openIntent: Intent = new Intent(this, classOf[Shadowsocks])
|
||||
openIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
@@ -373,6 +253,8 @@ class ShadowVpnService extends VpnService {
|
||||
val action = intent.getAction
|
||||
if (VpnService.SERVICE_INTERFACE == action) {
|
||||
return super.onBind(intent)
|
||||
} else if (classOf[ShadowsocksVpnService].getName == action) {
|
||||
return binder
|
||||
}
|
||||
null
|
||||
}
|
||||
@@ -380,55 +262,10 @@ class ShadowVpnService extends VpnService {
|
||||
override def onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
Config.refresh(this)
|
||||
|
||||
EasyTracker
|
||||
.getInstance(this)
|
||||
.send(MapBuilder
|
||||
.createEvent(TAG, "start", getVersionName, 0L)
|
||||
.set(Fields.SESSION_CONTROL, "start")
|
||||
.build())
|
||||
ConfigUtils.refresh(this)
|
||||
|
||||
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE)
|
||||
.asInstanceOf[NotificationManager]
|
||||
|
||||
// register close receiver
|
||||
val filter = new IntentFilter()
|
||||
filter.addAction(Intent.ACTION_SHUTDOWN)
|
||||
filter.addAction(Action.CLOSE)
|
||||
receiver = new BroadcastReceiver {
|
||||
def onReceive(p1: Context, p2: Intent) {
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
registerReceiver(receiver, filter)
|
||||
}
|
||||
|
||||
override def onDestroy() {
|
||||
killProcesses()
|
||||
|
||||
changeState(State.STOPPED)
|
||||
|
||||
EasyTracker
|
||||
.getInstance(this)
|
||||
.send(MapBuilder
|
||||
.createEvent(TAG, "stop", getVersionName, 0L)
|
||||
.set(Fields.SESSION_CONTROL, "stop")
|
||||
.build())
|
||||
|
||||
if (receiver != null) {
|
||||
unregisterReceiver(receiver)
|
||||
receiver = null
|
||||
}
|
||||
|
||||
if (conn != null) {
|
||||
conn.close()
|
||||
conn = null
|
||||
}
|
||||
|
||||
notificationManager.cancel(1)
|
||||
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
def killProcesses() {
|
||||
@@ -448,13 +285,115 @@ class ShadowVpnService extends VpnService {
|
||||
Utils.runCommand(sb.toString())
|
||||
}
|
||||
|
||||
override def onStart(intent: Intent, startId: Int) {
|
||||
handleCommand(intent)
|
||||
}
|
||||
|
||||
override def onStartCommand(intent: Intent, flags: Int, startId: Int): Int = {
|
||||
handleCommand(intent)
|
||||
Service.START_STICKY
|
||||
}
|
||||
|
||||
override def startRunner(c: Config) {
|
||||
|
||||
var config = c
|
||||
|
||||
// ensure the VPNService is prepared
|
||||
if (VpnService.prepare(this) != null) {
|
||||
val i = new Intent(this, classOf[ShadowVpnActivity])
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(i)
|
||||
return
|
||||
}
|
||||
|
||||
// start the tracker
|
||||
EasyTracker
|
||||
.getInstance(this)
|
||||
.send(MapBuilder
|
||||
.createEvent(TAG, "start", getVersionName, 0L)
|
||||
.set(Fields.SESSION_CONTROL, "start")
|
||||
.build())
|
||||
|
||||
// register close receiver
|
||||
val filter = new IntentFilter()
|
||||
filter.addAction(Intent.ACTION_SHUTDOWN)
|
||||
receiver = new BroadcastReceiver {
|
||||
def onReceive(p1: Context, p2: Intent) {
|
||||
stopRunner()
|
||||
}
|
||||
}
|
||||
registerReceiver(receiver, filter)
|
||||
|
||||
changeState(State.CONNECTING)
|
||||
|
||||
if (config.proxy == "198.199.101.152") {
|
||||
val container = getApplication.asInstanceOf[ShadowsocksApplication].tagContainer
|
||||
try {
|
||||
config = ConfigUtils.getPublicConfig(getBaseContext, container, config)
|
||||
} catch {
|
||||
case ex: Exception =>
|
||||
notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed))
|
||||
stopRunner()
|
||||
handler.sendEmptyMessageDelayed(Msg.CONNECT_FAIL, 500)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// reset the context
|
||||
killProcesses()
|
||||
|
||||
// Resolve the server address
|
||||
var resolved: Boolean = false
|
||||
if (!InetAddressUtils.isIPv4Address(config.proxy) &&
|
||||
!InetAddressUtils.isIPv6Address(config.proxy)) {
|
||||
Utils.resolve(config.proxy, enableIPv6 = true) match {
|
||||
case Some(addr) =>
|
||||
config.proxy = addr
|
||||
resolved = true
|
||||
case None => resolved = false
|
||||
}
|
||||
} else {
|
||||
resolved = true
|
||||
}
|
||||
|
||||
if (resolved && handleConnection) {
|
||||
handler.sendEmptyMessageDelayed(Msg.CONNECT_SUCCESS, 300)
|
||||
} else {
|
||||
notifyAlert(getString(R.string.forward_fail), getString(R.string.service_failed))
|
||||
handler.sendEmptyMessageDelayed(Msg.CONNECT_FAIL, 300)
|
||||
stopRunner()
|
||||
}
|
||||
handler.sendEmptyMessageDelayed(Msg.CONNECT_FINISH, 300)
|
||||
}
|
||||
|
||||
override def stopRunner() {
|
||||
|
||||
// channge the state
|
||||
changeState(State.STOPPED)
|
||||
|
||||
// stop the tracker
|
||||
EasyTracker
|
||||
.getInstance(this)
|
||||
.send(MapBuilder
|
||||
.createEvent(TAG, "stop", getVersionName, 0L)
|
||||
.set(Fields.SESSION_CONTROL, "stop")
|
||||
.build())
|
||||
|
||||
// clean up the context
|
||||
if (receiver != null) {
|
||||
unregisterReceiver(receiver)
|
||||
receiver = null
|
||||
}
|
||||
|
||||
// reset VPN
|
||||
killProcesses()
|
||||
|
||||
// close connections
|
||||
if (conn != null) {
|
||||
conn.close()
|
||||
conn = null
|
||||
}
|
||||
|
||||
// reset notifications
|
||||
notificationManager.cancel(1)
|
||||
}
|
||||
|
||||
override def getTag = TAG
|
||||
|
||||
override def getServiceMode = Mode.VPN
|
||||
}
|
||||
+33
-7
@@ -1,11 +1,12 @@
|
||||
package com.github.shadowsocks.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.{Intent, SharedPreferences, Context}
|
||||
import com.github.shadowsocks.ShadowsocksApplication
|
||||
import com.google.tagmanager.Container
|
||||
import scalaj.http.{HttpOptions, Http}
|
||||
import com.github.shadowsocks.aidl.Config
|
||||
|
||||
object Config {
|
||||
object ConfigUtils {
|
||||
val SHADOWSOCKS = "{\"server\": [%s], \"server_port\": %d, \"local_port\": %d, \"password\": %s, \"timeout\": %d}"
|
||||
val REDSOCKS = "base {" +
|
||||
" log_debug = off;" +
|
||||
@@ -83,10 +84,35 @@ object Config {
|
||||
val method = proxy(3).trim
|
||||
|
||||
new Config(config.isGlobalProxy, config.isGFWList, config.isBypassApps, config.isTrafficStat,
|
||||
config.profileName, host, password, method, port, config.localPort, config.proxiedAppString)
|
||||
config.profileName, host, password, method, config.proxiedAppString, port, config.localPort)
|
||||
}
|
||||
}
|
||||
|
||||
case class Config(isGlobalProxy: Boolean, isGFWList: Boolean, isBypassApps: Boolean,
|
||||
isTrafficStat: Boolean, profileName: String, var proxy: String, sitekey: String,
|
||||
encMethod: String, remotePort: Int, localPort: Int, proxiedAppString: String)
|
||||
def load(settings: SharedPreferences): Config = {
|
||||
val isGlobalProxy = settings.getBoolean(Key.isGlobalProxy, false)
|
||||
val isGFWList = settings.getBoolean(Key.isGFWList, false)
|
||||
val isBypassApps = settings.getBoolean(Key.isBypassApps, false)
|
||||
val isTrafficStat = settings.getBoolean(Key.isTrafficStat, false)
|
||||
|
||||
val profileName = settings.getString(Key.profileName, "default")
|
||||
val proxy = settings.getString(Key.proxy, "127.0.0.1")
|
||||
val sitekey = settings.getString(Key.sitekey, "default")
|
||||
val encMethod = settings.getString(Key.encMethod, "table")
|
||||
val remotePort: Int = try {
|
||||
settings.getString(Key.remotePort, "1984").toInt
|
||||
} catch {
|
||||
case ex: NumberFormatException =>
|
||||
1984
|
||||
}
|
||||
val localPort: Int = try {
|
||||
settings.getString(Key.localPort, "1984").toInt
|
||||
} catch {
|
||||
case ex: NumberFormatException =>
|
||||
1984
|
||||
}
|
||||
val proxiedAppString = settings.getString(Key.proxied, "")
|
||||
|
||||
new Config(isGlobalProxy, isGFWList, isBypassApps, isTrafficStat, profileName, proxy, sitekey,
|
||||
encMethod, proxiedAppString, remotePort, localPort)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,14 @@
|
||||
package com.github.shadowsocks.utils
|
||||
|
||||
import android.content.{Intent, SharedPreferences}
|
||||
import com.github.shadowsocks.aidl.Config
|
||||
|
||||
object Msg {
|
||||
val CONNECT_FINISH = 1
|
||||
val CONNECT_SUCCESS = 2
|
||||
val CONNECT_FAIL = 3
|
||||
val VPN_ERROR = 6
|
||||
}
|
||||
|
||||
object Path {
|
||||
val BASE = "/data/data/com.github.shadowsocks/"
|
||||
@@ -37,100 +45,21 @@ object Scheme {
|
||||
val SS = "ss"
|
||||
}
|
||||
|
||||
object Mode {
|
||||
val NAT = 0
|
||||
val VPN = 1
|
||||
}
|
||||
|
||||
object State {
|
||||
val INIT = 0
|
||||
val CONNECTING = 1
|
||||
val CONNECTED = 2
|
||||
val STOPPED = 3
|
||||
def isAvailable(state: Int): Boolean = state != CONNECTED && state != CONNECTING
|
||||
}
|
||||
|
||||
object Action {
|
||||
val CLOSE = "com.github.shadowsocks.ACTION_SHUTDOWN"
|
||||
val UPDATE_STATE = "com.github.shadowsocks.ACTION_UPDATE_STATE"
|
||||
val CLOSE = "com.github.shadowsocks.CLOSE"
|
||||
val UPDATE_FRAGMENT = "com.github.shadowsocks.ACTION_UPDATE_FRAGMENT"
|
||||
val UPDATE_PREFS = "com.github.shadowsocks.ACTION_UPDATE_PREFS"
|
||||
}
|
||||
|
||||
object Extra {
|
||||
val STATE = "state"
|
||||
val MESSAGE = "message"
|
||||
|
||||
def save(settings: SharedPreferences, config: Config) {
|
||||
val edit = settings.edit()
|
||||
|
||||
edit.putBoolean(Key.isGlobalProxy, config.isGlobalProxy)
|
||||
edit.putBoolean(Key.isGFWList, config.isGFWList)
|
||||
edit.putBoolean(Key.isBypassApps, config.isBypassApps)
|
||||
edit.putBoolean(Key.isTrafficStat, config.isTrafficStat)
|
||||
|
||||
edit.putString(Key.profileName, config.profileName)
|
||||
edit.putString(Key.proxy, config.proxy)
|
||||
edit.putString(Key.sitekey, config.sitekey)
|
||||
edit.putString(Key.encMethod, config.encMethod)
|
||||
edit.putString(Key.remotePort, config.remotePort.toString)
|
||||
edit.putString(Key.localPort, config.localPort.toString)
|
||||
|
||||
edit.apply()
|
||||
}
|
||||
|
||||
def get(intent: Intent): Config = {
|
||||
val isGlobalProxy = intent.getBooleanExtra(Key.isGlobalProxy, false)
|
||||
val isGFWList = intent.getBooleanExtra(Key.isGFWList, false)
|
||||
val isBypassApps = intent.getBooleanExtra(Key.isBypassApps, false)
|
||||
val isTrafficStat = intent.getBooleanExtra(Key.isTrafficStat, false)
|
||||
|
||||
val profileName = intent.getStringExtra(Key.profileName)
|
||||
val proxy = intent.getStringExtra(Key.proxy)
|
||||
val sitekey = intent.getStringExtra(Key.sitekey)
|
||||
val encMethod = intent.getStringExtra(Key.encMethod)
|
||||
val remotePort = intent.getIntExtra(Key.remotePort, 1984)
|
||||
val localPort = intent.getIntExtra(Key.localPort, 1984)
|
||||
val proxiedString = intent.getStringExtra(Key.proxied)
|
||||
|
||||
new Config(isGlobalProxy, isGFWList, isBypassApps, isTrafficStat, profileName, proxy, sitekey,
|
||||
encMethod, remotePort, localPort, proxiedString)
|
||||
}
|
||||
|
||||
def put(settings: SharedPreferences, intent: Intent) {
|
||||
val isGlobalProxy = settings.getBoolean(Key.isGlobalProxy, false)
|
||||
val isGFWList = settings.getBoolean(Key.isGFWList, false)
|
||||
val isBypassApps = settings.getBoolean(Key.isBypassApps, false)
|
||||
val isTrafficStat = settings.getBoolean(Key.isTrafficStat, false)
|
||||
|
||||
|
||||
val profileName = settings.getString(Key.profileName, "default")
|
||||
val proxy = settings.getString(Key.proxy, "127.0.0.1")
|
||||
val sitekey = settings.getString(Key.sitekey, "default")
|
||||
val encMethod = settings.getString(Key.encMethod, "table")
|
||||
val remotePort: Int = try {
|
||||
settings.getString(Key.remotePort, "1984").toInt
|
||||
} catch {
|
||||
case ex: NumberFormatException => {
|
||||
1984
|
||||
}
|
||||
}
|
||||
val localProt: Int = try {
|
||||
settings.getString(Key.localPort, "1984").toInt
|
||||
} catch {
|
||||
case ex: NumberFormatException => {
|
||||
1984
|
||||
}
|
||||
}
|
||||
val proxiedAppString = settings.getString(Key.proxied, "")
|
||||
|
||||
intent.putExtra(Key.isGlobalProxy, isGlobalProxy)
|
||||
intent.putExtra(Key.isGFWList, isGFWList)
|
||||
intent.putExtra(Key.isBypassApps, isBypassApps)
|
||||
intent.putExtra(Key.isTrafficStat, isTrafficStat)
|
||||
|
||||
intent.putExtra(Key.profileName, profileName)
|
||||
intent.putExtra(Key.proxy, proxy)
|
||||
intent.putExtra(Key.sitekey, sitekey)
|
||||
intent.putExtra(Key.encMethod, encMethod)
|
||||
intent.putExtra(Key.remotePort, remotePort)
|
||||
intent.putExtra(Key.localPort, localProt)
|
||||
|
||||
intent.putExtra(Key.proxied, proxiedAppString)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user