Commit c4833ddb authored by Max Lv's avatar Max Lv

add aidl

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