diff --git a/src/main/aidl/com/github/shadowsocks/aidl/Config.aidl b/src/main/aidl/com/github/shadowsocks/aidl/Config.aidl deleted file mode 100644 index 667e8d16..00000000 --- a/src/main/aidl/com/github/shadowsocks/aidl/Config.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package com.github.shadowsocks.aidl; - -parcelable Config; \ No newline at end of file diff --git a/src/main/aidl/com/github/shadowsocks/aidl/Config.java b/src/main/aidl/com/github/shadowsocks/aidl/Config.java deleted file mode 100644 index 13ec1ff8..00000000 --- a/src/main/aidl/com/github/shadowsocks/aidl/Config.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.github.shadowsocks.aidl; - -import android.os.Parcel; -import android.os.Parcelable; - -public class Config implements Parcelable { - - public boolean isProxyApps = false; - public boolean isBypassApps = false; - public boolean isUdpDns = false; - public boolean isAuth = false; - public boolean isIpv6 = false; - - public String profileName = "Untitled"; - public String proxy = "127.0.0.1"; - public String sitekey = "null"; - public String route = "all"; - - public String encMethod = "rc4"; - public String proxiedAppString = ""; - - public int remotePort = 1984; - public int localPort = 1080; - - public int profileId = 0; - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public Config createFromParcel(Parcel in) { - return new Config(in); - } - - public Config[] newArray(int size) { - return new Config[size]; - } - }; - - public Config(boolean isProxyApps, boolean isBypassApps, - boolean isUdpDns, boolean isAuth, boolean isIpv6, String profileName, String proxy, String sitekey, - String encMethod, String proxiedAppString, String route, int remotePort, int localPort, int profileId) { - this.isProxyApps = isProxyApps; - this.isBypassApps = isBypassApps; - this.isUdpDns = isUdpDns; - this.isAuth = isAuth; - this.isIpv6 = isIpv6; - this.profileName = profileName; - this.proxy = proxy; - this.sitekey = sitekey; - this.encMethod = encMethod; - this.proxiedAppString = proxiedAppString; - this.route = route; - this.remotePort = remotePort; - this.localPort = localPort; - this.profileId = profileId; - } - - private Config(Parcel in) { - readFromParcel(in); - } - - public void readFromParcel(Parcel in) { - isProxyApps = in.readInt() == 1; - isBypassApps = in.readInt() == 1; - isUdpDns = in.readInt() == 1; - isAuth = in.readInt() == 1; - isIpv6 = in.readInt() == 1; - profileName = in.readString(); - proxy = in.readString(); - sitekey = in.readString(); - encMethod = in.readString(); - proxiedAppString = in.readString(); - route = in.readString(); - remotePort = in.readInt(); - localPort = in.readInt(); - profileId = in.readInt(); - } - - @Override public int describeContents() { - return 0; - } - - @Override public void writeToParcel(Parcel out, int flags) { - out.writeInt(isProxyApps ? 1 : 0); - out.writeInt(isBypassApps ? 1 : 0); - out.writeInt(isUdpDns ? 1 : 0); - out.writeInt(isAuth ? 1 : 0); - out.writeInt(isIpv6 ? 1 : 0); - out.writeString(profileName); - out.writeString(proxy); - out.writeString(sitekey); - out.writeString(encMethod); - out.writeString(proxiedAppString); - out.writeString(route); - out.writeInt(remotePort); - out.writeInt(localPort); - out.writeInt(profileId); - } -} diff --git a/src/main/aidl/com/github/shadowsocks/aidl/IShadowsocksService.aidl b/src/main/aidl/com/github/shadowsocks/aidl/IShadowsocksService.aidl index 7b46ad9a..7245721e 100644 --- a/src/main/aidl/com/github/shadowsocks/aidl/IShadowsocksService.aidl +++ b/src/main/aidl/com/github/shadowsocks/aidl/IShadowsocksService.aidl @@ -1,6 +1,5 @@ package com.github.shadowsocks.aidl; -import com.github.shadowsocks.aidl.Config; import com.github.shadowsocks.aidl.IShadowsocksServiceCallback; interface IShadowsocksService { @@ -9,5 +8,5 @@ interface IShadowsocksService { oneway void registerCallback(IShadowsocksServiceCallback cb); oneway void unregisterCallback(IShadowsocksServiceCallback cb); - oneway void use(in Config config); + oneway void use(in int profileId); } diff --git a/src/main/res/xml/pref_all.xml b/src/main/res/xml/pref_all.xml index e29c44ec..fb1246ba 100644 --- a/src/main/res/xml/pref_all.xml +++ b/src/main/res/xml/pref_all.xml @@ -7,36 +7,43 @@ @@ -47,20 +54,24 @@ diff --git a/src/main/scala/com/github/shadowsocks/AppManager.scala b/src/main/scala/com/github/shadowsocks/AppManager.scala index 9eb02b36..54d9ea33 100644 --- a/src/main/scala/com/github/shadowsocks/AppManager.scala +++ b/src/main/scala/com/github/shadowsocks/AppManager.scala @@ -117,8 +117,10 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener { proxiedApps.add(item.packageName) check.setChecked(true) } - if (!appsLoading.get) - app.editor.putString(Key.proxied, proxiedApps.mkString("\n")).apply + if (!appsLoading.get) { + profile.individual = proxiedApps.mkString("\n") + app.profileManager.updateProfile(profile) + } } } @@ -141,9 +143,9 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener { private var loadingView: View = _ private val appsLoading = new AtomicBoolean private var handler: Handler = _ + private val profile = app.currentProfile.get - private def initProxiedApps(str: String = app.settings.getString(Key.proxied, "")) = - proxiedApps = str.split('\n').to[mutable.HashSet] + private def initProxiedApps(str: String = profile.individual) = proxiedApps = str.split('\n').to[mutable.HashSet] override def onDestroy() { instance = null @@ -225,17 +227,23 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener { toolbar.inflateMenu(R.menu.app_manager_menu) toolbar.setOnMenuItemClickListener(this) - app.editor.putBoolean(Key.isProxyApps, true).apply + if (!profile.proxyApps) { + profile.proxyApps = true + app.profileManager.updateProfile(profile) + } findViewById(R.id.onSwitch).asInstanceOf[Switch] .setOnCheckedChangeListener((_, checked) => { - app.editor.putBoolean(Key.isProxyApps, checked).apply + profile.proxyApps = checked + app.profileManager.updateProfile(profile) finish() }) bypassSwitch = findViewById(R.id.bypassSwitch).asInstanceOf[Switch] - bypassSwitch.setChecked(app.settings.getBoolean(Key.isBypassApps, false)) - bypassSwitch.setOnCheckedChangeListener((_, checked) => - app.editor.putBoolean(Key.isBypassApps, checked).apply()) + bypassSwitch.setChecked(profile.bypass) + bypassSwitch.setOnCheckedChangeListener((_, checked) => { + profile.bypass = checked + app.profileManager.updateProfile(profile) + }) initProxiedApps() loadingView = findViewById(R.id.loading) diff --git a/src/main/scala/com/github/shadowsocks/BaseService.scala b/src/main/scala/com/github/shadowsocks/BaseService.scala index 709d7a55..12c61eb4 100644 --- a/src/main/scala/com/github/shadowsocks/BaseService.scala +++ b/src/main/scala/com/github/shadowsocks/BaseService.scala @@ -47,17 +47,19 @@ import android.os.{Handler, RemoteCallbackList} import android.text.TextUtils import android.util.Log import android.widget.Toast -import com.github.shadowsocks.aidl.{Config, IShadowsocksService, IShadowsocksServiceCallback} +import com.github.kevinsawicki.http.HttpRequest +import com.github.shadowsocks.aidl.{IShadowsocksService, IShadowsocksServiceCallback} import com.github.shadowsocks.utils._ import com.github.shadowsocks.ShadowsocksApplication.app +import com.github.shadowsocks.database.Profile trait BaseService extends Service { @volatile private var state = State.STOPPED - @volatile protected var config: Config = null + @volatile protected var profile: Profile = _ - var timer: Timer = null - var trafficMonitorThread: TrafficMonitorThread = null + var timer: Timer = _ + var trafficMonitorThread: TrafficMonitorThread = _ final val callbacks = new RemoteCallbackList[IShadowsocksServiceCallback] var callbacksCount: Int = _ @@ -101,26 +103,50 @@ trait BaseService extends Service { } } - override def use(config: Config) = synchronized(state match { - case State.STOPPED => if (config != null && checkConfig(config)) startRunner(config) - case State.CONNECTED => - if (config == null) stopRunner(true) - else if (config.profileId != BaseService.this.config.profileId && checkConfig(config)) { + override def use(profileId: Int) = synchronized(if (profileId < 0) stopRunner(true) else { + val profile = app.profileManager.getProfile(profileId).orNull + if (profile == null) stopRunner(true) else state match { + case State.STOPPED => if (checkProfile(profile)) startRunner(profile) + case State.CONNECTED => if (profileId != BaseService.this.profile.id && checkProfile(profile)) { stopRunner(false) - startRunner(config) + startRunner(profile) } - case _ => Log.w(BaseService.this.getClass.getSimpleName, "Illegal state when invoking use: " + state) + case _ => Log.w(BaseService.this.getClass.getSimpleName, "Illegal state when invoking use: " + state) + } }) } - def checkConfig(config: Config) = if (TextUtils.isEmpty(config.proxy) || TextUtils.isEmpty(config.sitekey)) { + def checkProfile(profile: Profile) = if (TextUtils.isEmpty(profile.host) || TextUtils.isEmpty(profile.password)) { changeState(State.STOPPED) stopRunner(true) false } else true - def startRunner(config: Config) { - this.config = config + def connect() = if (profile.host == "198.199.101.152") try { + val holder = app.containerHolder + val container = holder.getContainer + val url = container.getString("proxy_url") + val sig = Utils.getSignature(this) + val list = HttpRequest + .post(url) + .connectTimeout(2000) + .readTimeout(2000) + .send("sig="+sig) + .body + val proxies = util.Random.shuffle(list.split('|').toSeq) + val proxy = proxies.head.split(':') + profile.host = proxy(0).trim + profile.remotePort = proxy(1).trim.toInt + profile.password = proxy(2).trim + profile.method = proxy(3).trim + } catch { + case ex: Exception => + changeState(State.STOPPED, getString(R.string.service_failed)) + stopRunner(true) + } + + def startRunner(profile: Profile) { + this.profile = profile startService(new Intent(this, getClass)) TrafficMonitor.reset() @@ -135,6 +161,12 @@ trait BaseService extends Service { registerReceiver(closeReceiver, filter) closeReceiverRegistered = true } + + app.track(getClass.getSimpleName, "start") + + changeState(State.CONNECTING) + + Utils.ThrowableFuture(connect) } def stopRunner(stopService: Boolean) { @@ -158,17 +190,19 @@ trait BaseService extends Service { // stop the service if nothing has bound to it if (stopService) stopSelf() + + profile = null } def updateTrafficTotal(tx: Long, rx: Long) { - val config = this.config // avoid race conditions without locking - if (config != null) { - app.profileManager.getProfile(config.profileId) match { - case Some(profile) => - profile.tx += tx - profile.rx += rx - app.profileManager.updateProfile(profile) - case None => // Ignore + val profile = this.profile // avoid race conditions without locking + if (profile != null) { + app.profileManager.getProfile(profile.id) match { + case Some(p) => // default profile may have host, etc. modified + p.tx += tx + p.rx += rx + app.profileManager.updateProfile(p) + case None => } } } @@ -197,6 +231,12 @@ trait BaseService extends Service { }) } + + override def onCreate() { + super.onCreate() + app.refreshContainerHolder + } + // Service of shadowsocks should always be started explicitly override def onStartCommand(intent: Intent, flags: Int, startId: Int): Int = Service.START_NOT_STICKY @@ -217,4 +257,15 @@ trait BaseService extends Service { state = s }) } + + def getBlackList = { + val default = getString(R.string.black_list) + try { + val container = app.containerHolder.getContainer + val update = container.getString("black_list") + if (update == null || update.isEmpty) default else update + } catch { + case ex: Exception => default + } + } } diff --git a/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala b/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala index d26f0fc3..63476a32 100644 --- a/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala +++ b/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala @@ -11,20 +11,19 @@ import android.provider.Settings import android.support.v7.app.{AlertDialog, AppCompatActivity} import android.support.v7.widget.RecyclerView.ViewHolder import android.support.v7.widget.Toolbar.OnMenuItemClickListener +import android.support.v7.widget._ import android.support.v7.widget.helper.ItemTouchHelper import android.support.v7.widget.helper.ItemTouchHelper.SimpleCallback -import android.support.v7.widget._ import android.text.style.TextAppearanceSpan import android.text.{SpannableStringBuilder, Spanned, TextUtils} -import android.util.Log import android.view._ import android.widget.{CheckedTextView, ImageView, LinearLayout, Toast} import com.github.clans.fab.{FloatingActionButton, FloatingActionMenu} +import com.github.shadowsocks.ShadowsocksApplication.app import com.github.shadowsocks.aidl.IShadowsocksServiceCallback import com.github.shadowsocks.database.Profile import com.github.shadowsocks.utils.{Key, Parser, TrafficMonitor, Utils} import com.github.shadowsocks.widget.UndoSnackbarManager -import com.github.shadowsocks.ShadowsocksApplication.app import net.glxn.qrgen.android.QRCode import scala.collection.mutable.ArrayBuffer @@ -281,8 +280,9 @@ final class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClic v.getId match { case R.id.fab_manual_add => menu.toggle(true) - app.profileManager.reload(-1) - app.switchProfile(app.profileManager.save.id) + val profile = app.profileManager.createProfile() + app.profileManager.updateProfile(profile) + app.switchProfile(profile.id) finish case R.id.fab_qrcode_add => menu.toggle(false) diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala index f5f9725f..61c410c6 100644 --- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala +++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala @@ -100,7 +100,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext { import Shadowsocks._ // Variables - var serviceStarted = false + var serviceStarted: Boolean = _ var fab: FloatingActionButton = _ var fabProgressCircle: FABProgressCircle = _ var progressDialog: ProgressDialog = _ @@ -186,23 +186,6 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext { if (!app.settings.getBoolean(app.getVersionName, false)) { app.editor.putBoolean(app.getVersionName, true).apply() - try { - // Workaround that convert port(String) to port(Int) - val oldLocalPort = app.settings.getString(Key.localPort, "") - val oldRemotePort = app.settings.getString(Key.remotePort, "") - - if (oldLocalPort != "") { - app.editor.putInt(Key.localPort, oldLocalPort.toInt).apply() - } - if (oldRemotePort != "") { - app.editor.putInt(Key.remotePort, oldRemotePort.toInt).apply() - } - } catch { - case ex: Exception => // Ignore - } - val oldProxiedApps = app.settings.getString(Key.proxied, "") - if (oldProxiedApps.contains('|')) - app.editor.putString(Key.proxied, DBHelper.updateProxiedApps(this, oldProxiedApps)).apply() recovery() @@ -364,7 +347,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext { val title = field.get(toolbar).asInstanceOf[TextView] title.setFocusable(true) title.setGravity(0x10) - title.getLayoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; + title.getLayoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT title.setOnClickListener(_ => startActivity(new Intent(this, classOf[ProfileManagerActivity]))) val typedArray = obtainStyledAttributes(Array(R.attr.selectableItemBackgroundBorderless)) title.setBackgroundResource(typedArray.getResourceId(0, 0)) @@ -434,11 +417,6 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext { handler.post(() => attachService) } - protected override def onPause() { - super.onPause() - app.profileManager.save - } - private def hideCircle() { try { fabProgressCircle.hide() @@ -489,34 +467,34 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext { private def updateCurrentProfile() = { // Check if current profile changed - if (app.profileId != currentProfile.id) { - currentProfile = app.currentProfile match { + if (preferences.profile == null || app.profileId != preferences.profile.id) { + updatePreferenceScreen(app.currentProfile match { case Some(profile) => profile // updated case None => // removed app.switchProfile((app.profileManager.getFirstProfile match { case Some(first) => first case None => app.profileManager.createDefault() }).id) - } - - updatePreferenceScreen() + }) if (serviceStarted) serviceLoad() true - } else false + } else { + preferences.refreshProfile() + false + } } protected override def onResume() { super.onResume() - ConfigUtils.refresh(this) + app.refreshContainerHolder updateState(updateCurrentProfile()) } - private def updatePreferenceScreen() { - val profile = currentProfile + private def updatePreferenceScreen(profile: Profile) { if (profile.host == "198.199.101.152") if (adView == null) { adView = new AdView(this) adView.setAdUnitId("ca-app-pub-9097031975646651/7760346322") @@ -525,7 +503,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext { adView.loadAd(new AdRequest.Builder().build()) } else adView.setVisibility(View.VISIBLE) else if (adView != null) adView.setVisibility(View.GONE) - preferences.update(profile) + preferences.setProfile(profile) } override def onStart() { @@ -587,7 +565,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext { } def serviceStop() { - if (bgService != null) bgService.use(null) + if (bgService != null) bgService.use(-1) } def checkText(key: String): Boolean = { @@ -599,7 +577,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext { /** Called when connect button is clicked. */ def serviceLoad() { - bgService.use(ConfigUtils.loadFromSharedPreferences) + bgService.use(app.profileId) if (app.isVpnEnabled) { changeSwitch(checked = false) diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala index f10c607c..200606dc 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala @@ -60,13 +60,12 @@ object ShadowsocksApplication { class ShadowsocksApplication extends Application { import ShadowsocksApplication._ - lazy val dbHelper = new DBHelper(this) final val SIG_FUNC = "getSignature" var containerHolder: ContainerHolder = _ lazy val tracker = GoogleAnalytics.getInstance(this).newTracker(R.xml.tracker) lazy val settings = PreferenceManager.getDefaultSharedPreferences(this) lazy val editor = settings.edit - lazy val profileManager = new ProfileManager(settings, dbHelper) + lazy val profileManager = new ProfileManager(new DBHelper(this)) def isNatEnabled = settings.getBoolean(Key.isNAT, false) def isVpnEnabled = !isNatEnabled @@ -85,12 +84,12 @@ class ShadowsocksApplication extends Application { .build()) def profileId = settings.getInt(Key.profileId, -1) - def profileId(i: Int) = settings.edit.putInt(Key.profileId, i).apply + def profileId(i: Int) = editor.putInt(Key.profileId, i).apply def currentProfile = profileManager.getProfile(profileId) def switchProfile(id: Int) = { profileId(id) - profileManager.load(id) + profileManager.getProfile(id) getOrElse profileManager.createProfile() } override def onCreate() { @@ -117,4 +116,9 @@ class ShadowsocksApplication extends Application { } pending.setResultCallback(callback, 2, TimeUnit.SECONDS) } + + def refreshContainerHolder { + val holder = app.containerHolder + if (holder != null) holder.refresh() + } } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala index 4774996a..90390661 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala @@ -48,9 +48,9 @@ import android.content._ import android.net.{ConnectivityManager, Network} import android.os._ import android.util.{Log, SparseArray} -import com.github.shadowsocks.aidl.Config import com.github.shadowsocks.utils._ import com.github.shadowsocks.ShadowsocksApplication.app +import com.github.shadowsocks.database.Profile import scala.collection.JavaConversions._ import scala.collection.mutable.ArrayBuffer @@ -144,22 +144,22 @@ class ShadowsocksNatService extends BaseService { } def startShadowsocksDaemon() { - if (config.route != Route.ALL) { - val acl: Array[Array[String]] = config.route match { + if (profile.route != Route.ALL) { + val acl: Array[Array[String]] = profile.route match { case Route.BYPASS_LAN => Array(getResources.getStringArray(R.array.private_route)) case Route.BYPASS_CHN => Array(getResources.getStringArray(R.array.chn_route)) case Route.BYPASS_LAN_CHN => Array(getResources.getStringArray(R.array.private_route), getResources.getStringArray(R.array.chn_route)) } - ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/acl.list"))(p => { + Utils.printToFile(new File(getApplicationInfo.dataDir + "/acl.list"))(p => { acl.flatten.foreach(p.println) }) } val conf = ConfigUtils - .SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, config.localPort, - config.sitekey, config.encMethod, 600) - ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/ss-local-nat.conf"))(p => { + .SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, profile.localPort, + profile.password, profile.method, 600) + Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-local-nat.conf"))(p => { p.println(conf) }) @@ -170,9 +170,9 @@ class ShadowsocksNatService extends BaseService { , "-P", getApplicationInfo.dataDir , "-c" , getApplicationInfo.dataDir + "/ss-local-nat.conf") - if (config.isAuth) cmd += "-A" + if (profile.auth) cmd += "-A" - if (config.route != Route.ALL) { + if (profile.route != Route.ALL) { cmd += "--acl" cmd += (getApplicationInfo.dataDir + "/acl.list") } @@ -182,11 +182,11 @@ class ShadowsocksNatService extends BaseService { } def startTunnel() { - if (config.isUdpDns) { + if (profile.udpdns) { val conf = ConfigUtils - .SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, 8153, - config.sitekey, config.encMethod, 10) - ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-nat.conf"))(p => { + .SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, 8153, + profile.password, profile.method, 10) + Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-nat.conf"))(p => { p.println(conf) }) val cmd = new ArrayBuffer[String] @@ -200,7 +200,7 @@ class ShadowsocksNatService extends BaseService { cmd += ("-l" , "8153") - if (config.isAuth) cmd += "-A" + if (profile.auth) cmd += "-A" if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" ")) @@ -208,9 +208,9 @@ class ShadowsocksNatService extends BaseService { } else { val conf = ConfigUtils - .SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, 8163, - config.sitekey, config.encMethod, 10) - ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-nat.conf"))(p => { + .SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, 8163, + profile.password, profile.method, 10) + Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-nat.conf"))(p => { p.println(conf) }) val cmdBuf = new ArrayBuffer[String] @@ -223,7 +223,7 @@ class ShadowsocksNatService extends BaseService { , "-P", getApplicationInfo.dataDir , "-c" , getApplicationInfo.dataDir + "/ss-tunnel-nat.conf") - if (config.isAuth) cmdBuf += "-A" + if (profile.auth) cmdBuf += "-A" if (BuildConfig.DEBUG) Log.d(TAG, cmdBuf.mkString(" ")) @@ -233,16 +233,15 @@ class ShadowsocksNatService extends BaseService { def startDnsDaemon() { - val conf = if (config.route == Route.BYPASS_CHN || config.route == Route.BYPASS_LAN_CHN) { - val blackList = ConfigUtils.getBlackList(this) + val conf = if (profile.route == Route.BYPASS_CHN || profile.route == Route.BYPASS_LAN_CHN) { ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, - "127.0.0.1", 8153, blackList, 8163, "") + "127.0.0.1", 8153, getBlackList, 8163, "") } else { ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, "127.0.0.1", 8153, 8163, "") } - ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/pdnsd-nat.conf"))(p => { + Utils.printToFile(new File(getApplicationInfo.dataDir + "/pdnsd-nat.conf"))(p => { p.println(conf) }) val cmd = getApplicationInfo.dataDir + "/pdnsd -c " + getApplicationInfo.dataDir + "/pdnsd-nat.conf" @@ -253,10 +252,10 @@ class ShadowsocksNatService extends BaseService { } def startRedsocksDaemon() { - val conf = ConfigUtils.REDSOCKS.formatLocal(Locale.ENGLISH, config.localPort) + val conf = ConfigUtils.REDSOCKS.formatLocal(Locale.ENGLISH, profile.localPort) val cmd = "%s/redsocks -c %s/redsocks-nat.conf" .formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, getApplicationInfo.dataDir) - ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/redsocks-nat.conf"))(p => { + Utils.printToFile(new File(getApplicationInfo.dataDir + "/redsocks-nat.conf"))(p => { p.println(conf) }) @@ -268,7 +267,7 @@ class ShadowsocksNatService extends BaseService { def handleConnection: Boolean = { startTunnel() - if (!config.isUdpDns) startDnsDaemon() + if (!profile.udpdns) startDnsDaemon() startRedsocksDaemon() startShadowsocksDaemon() setupIptables() @@ -285,11 +284,6 @@ class ShadowsocksNatService extends BaseService { } } - override def onCreate() { - super.onCreate() - ConfigUtils.refresh(this) - } - def killProcesses() { if (sslocalProcess != null) { sslocalProcess.destroy() @@ -319,8 +313,8 @@ class ShadowsocksNatService extends BaseService { init_sb.append(Utils.iptables + " -t nat -F OUTPUT") val cmd_bypass = Utils.iptables + CMD_IPTABLES_RETURN - if (!InetAddress.getByName(config.proxy.toUpperCase).isInstanceOf[Inet6Address]) { - init_sb.append(cmd_bypass.replace("-p tcp -d 0.0.0.0", "-d " + config.proxy)) + if (!InetAddress.getByName(profile.host.toUpperCase).isInstanceOf[Inet6Address]) { + init_sb.append(cmd_bypass.replace("-p tcp -d 0.0.0.0", "-d " + profile.host)) } init_sb.append(cmd_bypass.replace("-p tcp -d 0.0.0.0", "-d 127.0.0.1")) init_sb.append(cmd_bypass.replace("-p tcp -d 0.0.0.0", "-m owner --uid-owner " + myUid)) @@ -329,14 +323,14 @@ class ShadowsocksNatService extends BaseService { init_sb.append(Utils.iptables + " -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:8153") - if (!config.isProxyApps || config.isBypassApps) { + if (!profile.proxyApps || profile.bypass) { http_sb.append(Utils.iptables + CMD_IPTABLES_DNAT_ADD_SOCKS) } - if (config.isProxyApps) { + if (profile.proxyApps) { val uidMap = getPackageManager.getInstalledApplications(0).map(ai => ai.packageName -> ai.uid).toMap - for (pn <- config.proxiedAppString.split('\n')) uidMap.get(pn) match { + for (pn <- profile.individual.split('\n')) uidMap.get(pn) match { case Some(uid) => - if (!config.isBypassApps) { + if (!profile.bypass) { http_sb.append((Utils.iptables + CMD_IPTABLES_DNAT_ADD_SOCKS) .replace("-t nat", "-t nat -m owner --uid-owner " + uid)) } else { @@ -348,59 +342,46 @@ class ShadowsocksNatService extends BaseService { Console.runRootCommand((init_sb ++ http_sb).toArray) } - override def startRunner(config: Config) { + override def startRunner(profile: Profile) { if (!Console.isRoot) { changeState(State.STOPPED, getString(R.string.nat_no_root)) + stopRunner(true) return } - super.startRunner(config) + super.startRunner(profile) + } - app.track(TAG, "start") - - changeState(State.CONNECTING) + override def connect() { + super.connect() - Utils.ThrowableFuture { - if (config.proxy == "198.199.101.152") { - val holder = app.containerHolder - try { - this.config = ConfigUtils.getPublicConfig(getBaseContext, holder.getContainer, config) - } catch { - case ex: Exception => - changeState(State.STOPPED, getString(R.string.service_failed)) - stopRunner(true) - this.config = null + if (this.profile != null) { + + // Clean up + killProcesses() + + var resolved: Boolean = false + if (!Utils.isNumeric(this.profile.host)) { + Utils.resolve(this.profile.host, enableIPv6 = true) match { + case Some(a) => + this.profile.host = a + resolved = true + case None => resolved = false } + } else { + resolved = true } - if (this.config != null) { - - // Clean up - killProcesses() - - var resolved: Boolean = false - if (!Utils.isNumeric(config.proxy)) { - Utils.resolve(config.proxy, enableIPv6 = true) match { - case Some(a) => - config.proxy = a - resolved = true - case None => resolved = false - } - } else { - resolved = true - } - - if (!resolved) { - changeState(State.STOPPED, getString(R.string.invalid_server)) - stopRunner(true) - } else if (handleConnection) { - // Set DNS - flushDns() - changeState(State.CONNECTED) - notification = new ShadowsocksNotification(this, config.profileName, true) - } else { - changeState(State.STOPPED, getString(R.string.service_failed)) - stopRunner(true) - } + if (!resolved) { + changeState(State.STOPPED, getString(R.string.invalid_server)) + stopRunner(true) + } else if (handleConnection) { + // Set DNS + flushDns() + changeState(State.CONNECTED) + notification = new ShadowsocksNotification(this, profile.name, true) + } else { + changeState(State.STOPPED, getString(R.string.service_failed)) + stopRunner(true) } } } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala index bc71eb50..7d52e9df 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala @@ -66,7 +66,7 @@ class ShadowsocksRunnerActivity extends Activity with ServiceBoundContext { def startBackgroundService() { if (app.isNatEnabled) { - bgService.use(ConfigUtils.loadFromSharedPreferences) + bgService.use(app.profileId) finish() } else { val intent = VpnService.prepare(ShadowsocksRunnerActivity.this) @@ -109,7 +109,7 @@ class ShadowsocksRunnerActivity extends Activity with ServiceBoundContext { resultCode match { case Activity.RESULT_OK => if (bgService != null) { - bgService.use(ConfigUtils.loadFromSharedPreferences) + bgService.use(app.profileId) } case _ => Log.e(TAG, "Failed to start VpnService") diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerService.scala index 929b6161..7613556e 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerService.scala @@ -58,11 +58,11 @@ class ShadowsocksRunnerService extends Service with ServiceBoundContext { } def startBackgroundService() { - if (app.isNatEnabled) bgService.use(ConfigUtils.loadFromSharedPreferences) else { + if (app.isNatEnabled) bgService.use(app.profileId) else { val intent = VpnService.prepare(ShadowsocksRunnerService.this) if (intent == null) { if (bgService != null) { - bgService.use(ConfigUtils.loadFromSharedPreferences) + bgService.use(app.profileId) } } } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala index 61ef14c3..4f50faaa 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala @@ -9,10 +9,10 @@ import android.os.{Build, Bundle} import android.preference.{Preference, PreferenceFragment, SwitchPreference} import android.support.v7.app.AlertDialog import android.webkit.{WebView, WebViewClient} +import com.github.shadowsocks.ShadowsocksApplication.app import com.github.shadowsocks.database.Profile import com.github.shadowsocks.preferences._ import com.github.shadowsocks.utils.{Key, Utils} -import com.github.shadowsocks.ShadowsocksApplication.app object ShadowsocksSettings { // Constants @@ -74,12 +74,58 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan addPreferencesFromResource(R.xml.pref_all) getPreferenceManager.getSharedPreferences.registerOnSharedPreferenceChangeListener(this) + findPreference(Key.profileName).setOnPreferenceChangeListener((_, value) => { + profile.name = value.asInstanceOf[String] + app.profileManager.updateProfile(profile) + }) + findPreference(Key.proxy).setOnPreferenceChangeListener((_, value) => { + profile.host = value.asInstanceOf[String] + app.profileManager.updateProfile(profile) + }) + findPreference(Key.remotePort).setOnPreferenceChangeListener((_, value) => { + profile.remotePort = value.asInstanceOf[Int] + app.profileManager.updateProfile(profile) + }) + findPreference(Key.localPort).setOnPreferenceChangeListener((_, value) => { + profile.localPort = value.asInstanceOf[Int] + app.profileManager.updateProfile(profile) + }) + findPreference(Key.sitekey).setOnPreferenceChangeListener((_, value) => { + profile.password = value.asInstanceOf[String] + app.profileManager.updateProfile(profile) + }) + findPreference(Key.encMethod).setOnPreferenceChangeListener((_, value) => { + profile.method = value.asInstanceOf[String] + app.profileManager.updateProfile(profile) + }) + findPreference(Key.route).setOnPreferenceChangeListener((_, value) => { + profile.route = value.asInstanceOf[String] + app.profileManager.updateProfile(profile) + }) + isProxyApps = findPreference(Key.isProxyApps).asInstanceOf[SwitchPreference] - isProxyApps.setOnPreferenceClickListener((preference: Preference) => { + isProxyApps.setOnPreferenceClickListener(_ => { startActivity(new Intent(activity, classOf[AppManager])) isProxyApps.setChecked(true) false }) + isProxyApps.setOnPreferenceChangeListener((_, value) => { + profile.proxyApps = value.asInstanceOf[Boolean] + app.profileManager.updateProfile(profile) + }) + + findPreference(Key.isUdpDns).setOnPreferenceChangeListener((_, value) => { + profile.udpdns = value.asInstanceOf[Boolean] + app.profileManager.updateProfile(profile) + }) + findPreference(Key.isAuth).setOnPreferenceChangeListener((_, value) => { + profile.auth = value.asInstanceOf[Boolean] + app.profileManager.updateProfile(profile) + }) + findPreference(Key.isIpv6).setOnPreferenceChangeListener((_, value) => { + profile.ipv6 = value.asInstanceOf[Boolean] + app.profileManager.updateProfile(profile) + }) val switch = findPreference(Key.isAutoConnect).asInstanceOf[SwitchPreference] switch.setOnPreferenceChangeListener((_, value) => { @@ -131,9 +177,9 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan }) } - override def onResume { - super.onResume() - isProxyApps.setChecked(app.settings.getBoolean(Key.isProxyApps, false)) // update + def refreshProfile() { + profile = app.currentProfile.get + isProxyApps.setChecked(profile.proxyApps) } override def onDestroy { @@ -160,14 +206,9 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan } } - def update(profile: Profile) { - for (name <- PROXY_PREFS) { - val pref = findPreference(name) - updatePreference(pref, name, profile) - } - for (name <- FEATURE_PREFS) { - val pref = findPreference(name) - updatePreference(pref, name, profile) - } + var profile: Profile = _ + def setProfile(profile: Profile) { + this.profile = profile + for (name <- Array(PROXY_PREFS, FEATURE_PREFS).flatten) updatePreference(findPreference(name), name, profile) } } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala index a0359a94..b7a38c51 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala @@ -48,9 +48,9 @@ import android.content.pm.PackageManager.NameNotFoundException import android.net.VpnService import android.os._ import android.util.Log -import com.github.shadowsocks.aidl.Config import com.github.shadowsocks.utils._ import com.github.shadowsocks.ShadowsocksApplication.app +import com.github.shadowsocks.database.Profile import scala.collection.mutable.ArrayBuffer @@ -78,11 +78,6 @@ class ShadowsocksVpnService extends VpnService with BaseService { null } - override def onCreate() { - super.onCreate() - ConfigUtils.refresh(this) - } - override def onRevoke() { stopRunner(true) } @@ -132,66 +127,53 @@ class ShadowsocksVpnService extends VpnService with BaseService { } } - override def startRunner(config: Config) { - - super.startRunner(config) - - vpnThread = new ShadowsocksVpnThread(this) - vpnThread.start() + override def startRunner(profile: Profile) { // ensure the VPNService is prepared if (VpnService.prepare(this) != null) { val i = new Intent(this, classOf[ShadowsocksRunnerActivity]) i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) startActivity(i) + stopRunner(true) return } - app.track(TAG, "start") + super.startRunner(profile) + } - changeState(State.CONNECTING) + override def connect() = { + super.connect() - Utils.ThrowableFuture { - if (config.proxy == "198.199.101.152") { - val holder = app.containerHolder - try { - this.config = ConfigUtils.getPublicConfig(getBaseContext, holder.getContainer, config) - } catch { - case ex: Exception => - changeState(State.STOPPED, getString(R.string.service_failed)) - stopRunner(true) - this.config = null + if (profile != null) { + + vpnThread = new ShadowsocksVpnThread(this) + vpnThread.start() + + // reset the context + killProcesses() + + // Resolve the server address + var resolved: Boolean = false + if (!Utils.isNumeric(profile.host)) { + Utils.resolve(profile.host, enableIPv6 = true) match { + case Some(addr) => + profile.host = addr + resolved = true + case None => resolved = false } + } else { + resolved = true } - if (config != null) { - - // reset the context - killProcesses() - - // Resolve the server address - var resolved: Boolean = false - if (!Utils.isNumeric(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) { - changeState(State.STOPPED, getString(R.string.invalid_server)) - stopRunner(true) - } else if (handleConnection) { - changeState(State.CONNECTED) - notification = new ShadowsocksNotification(this, config.profileName) - } else { - changeState(State.STOPPED, getString(R.string.service_failed)) - stopRunner(true) - } + if (!resolved) { + changeState(State.STOPPED, getString(R.string.invalid_server)) + stopRunner(true) + } else if (handleConnection) { + changeState(State.CONNECTED) + notification = new ShadowsocksNotification(this, profile.name) + } else { + changeState(State.STOPPED, getString(R.string.service_failed)) + stopRunner(true) } } } @@ -199,7 +181,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { /** Called when the activity is first created. */ def handleConnection: Boolean = { startShadowsocksDaemon() - if (!config.isUdpDns) { + if (!profile.udpdns) { startDnsDaemon() startDnsTunnel() } @@ -210,22 +192,22 @@ class ShadowsocksVpnService extends VpnService with BaseService { def startShadowsocksDaemon() { - if (config.route != Route.ALL) { - val acl: Array[Array[String]] = config.route match { + if (profile.route != Route.ALL) { + val acl: Array[Array[String]] = profile.route match { case Route.BYPASS_LAN => Array(getResources.getStringArray(R.array.private_route)) case Route.BYPASS_CHN => Array(getResources.getStringArray(R.array.chn_route)) case Route.BYPASS_LAN_CHN => Array(getResources.getStringArray(R.array.private_route), getResources.getStringArray(R.array.chn_route)) } - ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/acl.list"))(p => { + Utils.printToFile(new File(getApplicationInfo.dataDir + "/acl.list"))(p => { acl.flatten.foreach(p.println) }) } val conf = ConfigUtils - .SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, config.localPort, - config.sitekey, config.encMethod, 600) - ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/ss-local-vpn.conf"))(p => { + .SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, profile.localPort, + profile.password, profile.method, 600) + Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-local-vpn.conf"))(p => { p.println(conf) }) @@ -236,9 +218,9 @@ class ShadowsocksVpnService extends VpnService with BaseService { , "-P", getApplicationInfo.dataDir , "-c", getApplicationInfo.dataDir + "/ss-local-vpn.conf") - if (config.isAuth) cmd += "-A" + if (profile.auth) cmd += "-A" - if (config.route != Route.ALL) { + if (profile.route != Route.ALL) { cmd += "--acl" cmd += (getApplicationInfo.dataDir + "/acl.list") } @@ -250,9 +232,9 @@ class ShadowsocksVpnService extends VpnService with BaseService { def startDnsTunnel() = { val conf = ConfigUtils - .SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, 8163, - config.sitekey, config.encMethod, 10) - ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-vpn.conf"))(p => { + .SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, 8163, + profile.password, profile.method, 10) + Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-vpn.conf"))(p => { p.println(conf) }) val cmd = new ArrayBuffer[String] @@ -266,7 +248,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { , "-P", getApplicationInfo.dataDir , "-c", getApplicationInfo.dataDir + "/ss-tunnel-vpn.conf") - if (config.isAuth) cmd += "-A" + if (profile.auth) cmd += "-A" if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" ")) @@ -274,18 +256,17 @@ class ShadowsocksVpnService extends VpnService with BaseService { } def startDnsDaemon() { - val ipv6 = if (config.isIpv6) "" else "reject = ::/0;" + val ipv6 = if (profile.ipv6) "" else "reject = ::/0;" val conf = { - if (config.route == Route.BYPASS_CHN || config.route == Route.BYPASS_LAN_CHN) { - val blackList = ConfigUtils.getBlackList(this) + if (profile.route == Route.BYPASS_CHN || profile.route == Route.BYPASS_LAN_CHN) { ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, - "0.0.0.0", 8153, blackList, 8163, ipv6) + "0.0.0.0", 8153, getBlackList, 8163, ipv6) } else { ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, "0.0.0.0", 8153, 8163, ipv6) } } - ConfigUtils.printToFile(new File(getApplicationInfo.dataDir + "/pdnsd-vpn.conf"))(p => { + Utils.printToFile(new File(getApplicationInfo.dataDir + "/pdnsd-vpn.conf"))(p => { p.println(conf) }) val cmd = getApplicationInfo.dataDir + "/pdnsd -c " + getApplicationInfo.dataDir + "/pdnsd-vpn.conf" @@ -299,22 +280,22 @@ class ShadowsocksVpnService extends VpnService with BaseService { val builder = new Builder() builder - .setSession(config.profileName) + .setSession(profile.name) .setMtu(VPN_MTU) .addAddress(PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"), 24) .addDnsServer("8.8.8.8") - if (config.isIpv6) { + if (profile.ipv6) { builder.addAddress(PRIVATE_VLAN6.formatLocal(Locale.ENGLISH, "1"), 126) builder.addRoute("::", 0) } if (Utils.isLollipopOrAbove) { - if (config.isProxyApps) { - for (pkg <- config.proxiedAppString.split('\n')) { + if (profile.proxyApps) { + for (pkg <- profile.individual.split('\n')) { try { - if (!config.isBypassApps) { + if (!profile.bypass) { builder.addAllowedApplication(pkg) } else { builder.addDisallowedApplication(pkg) @@ -327,7 +308,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { } } - if (config.route == Route.ALL || config.route == Route.BYPASS_CHN) { + if (profile.route == Route.ALL || profile.route == Route.BYPASS_CHN) { builder.addRoute("0.0.0.0", 0) } else { val privateList = getResources.getStringArray(R.array.bypass_private_route) @@ -368,12 +349,12 @@ class ShadowsocksVpnService extends VpnService with BaseService { + "--loglevel 3") .formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "2"), - config.localPort, fd, VPN_MTU, getApplicationInfo.dataDir + "/sock_path") + profile.localPort, fd, VPN_MTU, getApplicationInfo.dataDir + "/sock_path") - if (config.isIpv6) + if (profile.ipv6) cmd += " --netif-ip6addr " + PRIVATE_VLAN6.formatLocal(Locale.ENGLISH, "2") - if (config.isUdpDns) + if (profile.udpdns) cmd += " --enable-udprelay" else cmd += " --dnsgw %s:8153".formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1")) diff --git a/src/main/scala/com/github/shadowsocks/database/ProfileManager.scala b/src/main/scala/com/github/shadowsocks/database/ProfileManager.scala index 9e178e5c..76a59d57 100644 --- a/src/main/scala/com/github/shadowsocks/database/ProfileManager.scala +++ b/src/main/scala/com/github/shadowsocks/database/ProfileManager.scala @@ -39,16 +39,14 @@ package com.github.shadowsocks.database -import android.content.SharedPreferences import android.util.Log import com.github.shadowsocks.ShadowsocksApplication.app -import com.github.shadowsocks.utils.Key object ProfileManager { private final val TAG = "ProfileManager" } -class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) { +class ProfileManager(dbHelper: DBHelper) { import ProfileManager._ var profileAddedListener: Profile => Any = _ @@ -139,67 +137,6 @@ class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) { } } - def reload(id: Int): Profile = { - save() - load(id) - } - - def load(id: Int): Profile = { - - val profile = getProfile(id) getOrElse createProfile() - - val edit = settings.edit() - edit.putBoolean(Key.isProxyApps, profile.proxyApps) - edit.putBoolean(Key.isBypassApps, profile.bypass) - edit.putBoolean(Key.isUdpDns, profile.udpdns) - edit.putBoolean(Key.isAuth, profile.auth) - edit.putBoolean(Key.isIpv6, profile.ipv6) - edit.putString(Key.profileName, profile.name) - edit.putString(Key.proxy, profile.host) - edit.putString(Key.sitekey, profile.password) - edit.putString(Key.encMethod, profile.method) - edit.putInt(Key.remotePort, profile.remotePort) - edit.putInt(Key.localPort, profile.localPort) - edit.putString(Key.proxied, profile.individual) - edit.putInt(Key.profileId, profile.id) - edit.putString(Key.route, profile.route) - edit.apply() - - profile - } - - private def loadFromPreferences: Profile = { - - val id = settings.getInt(Key.profileId, -1) - - val profile: Profile = getProfile(id) match { - case Some(p) => p - case _ => new Profile() - } - - profile.proxyApps = settings.getBoolean(Key.isProxyApps, false) - profile.bypass = settings.getBoolean(Key.isBypassApps, false) - profile.udpdns = settings.getBoolean(Key.isUdpDns, false) - profile.auth = settings.getBoolean(Key.isAuth, false) - profile.ipv6 = settings.getBoolean(Key.isIpv6, false) - profile.name = settings.getString(Key.profileName, "default") - profile.host = settings.getString(Key.proxy, "127.0.0.1") - profile.password = settings.getString(Key.sitekey, "default") - profile.method = settings.getString(Key.encMethod, "table") - profile.route = settings.getString(Key.route, "all") - profile.remotePort = settings.getInt(Key.remotePort, 1984) - profile.localPort = settings.getInt(Key.localPort, 1984) - profile.individual = settings.getString(Key.proxied, "") - - profile - } - - def save(): Profile = { - val profile = loadFromPreferences - updateProfile(profile) - profile - } - def createDefault(): Profile = { val profile = new Profile { name = "Default" diff --git a/src/main/scala/com/github/shadowsocks/utils/ConfigUtils.scala b/src/main/scala/com/github/shadowsocks/utils/ConfigUtils.scala deleted file mode 100644 index ddcbb243..00000000 --- a/src/main/scala/com/github/shadowsocks/utils/ConfigUtils.scala +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Shadowsocks - A shadowsocks client for Android - * Copyright (C) 2014 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * - * ___====-_ _-====___ - * _--^^^#####// \\#####^^^--_ - * _-^##########// ( ) \\##########^-_ - * -############// |\^^/| \\############- - * _/############// (@::@) \\############\_ - * /#############(( \\// ))#############\ - * -###############\\ (oo) //###############- - * -#################\\ / VV \ //#################- - * -###################\\/ \//###################- - * _#/|##########/\######( /\ )######/\##########|\#_ - * |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \| - * ` |/ V V ` V \#\| | | |/#/ V ' V V \| ' - * ` ` ` ` / | | | | \ ' ' ' ' - * ( | | | | ) - * __\ | | | | /__ - * (vvv(VVV)(VVV)vvv) - * - * HERE BE DRAGONS - * - */ - -package com.github.shadowsocks.utils - -import android.content.Context -import com.github.kevinsawicki.http.HttpRequest -import com.github.shadowsocks.R -import com.github.shadowsocks.ShadowsocksApplication.app -import com.github.shadowsocks.aidl.Config -import com.google.android.gms.tagmanager.Container - -object ConfigUtils { - val SHADOWSOCKS = "{\"server\": \"%s\", \"server_port\": %d, \"local_port\": %d, \"password\": \"%s\", \"method\":\"%s\", \"timeout\": %d}" - val REDSOCKS = "base {\n" + - " log_debug = off;\n" + - " log_info = off;\n" + - " log = stderr;\n" + - " daemon = off;\n" + - " redirector = iptables;\n" + - "}\n" + - "redsocks {\n" + - " local_ip = 127.0.0.1;\n" + - " local_port = 8123;\n" + - " ip = 127.0.0.1;\n" + - " port = %d;\n" + - " type = socks5;\n" + - "}\n" - - val PDNSD_LOCAL = - """ - |global { - | perm_cache = 2048; - | cache_dir = "%s"; - | server_ip = %s; - | server_port = %d; - | query_method = tcp_only; - | run_ipv4 = on; - | min_ttl = 15m; - | max_ttl = 1w; - | timeout = 10; - | daemon = off; - |} - | - |server { - | label = "local"; - | ip = 127.0.0.1; - | port = %d; - | %s - | reject_policy = negate; - | reject_recursively = on; - | timeout = 5; - |} - | - |rr { - | name=localhost; - | reverse=on; - | a=127.0.0.1; - | owner=localhost; - | soa=localhost,root.localhost,42,86400,900,86400,86400; - |} - """.stripMargin - - val PDNSD_DIRECT = - """ - |global { - | perm_cache = 2048; - | cache_dir = "%s"; - | server_ip = %s; - | server_port = %d; - | query_method = tcp_only; - | run_ipv4 = on; - | min_ttl = 15m; - | max_ttl = 1w; - | timeout = 10; - | daemon = off; - |} - | - |server { - | label = "china-servers"; - | ip = 114.114.114.114, 112.124.47.27; - | timeout = 4; - | exclude = %s; - | policy = included; - | uptest = none; - | preset = on; - |} - | - |server { - | label = "local-server"; - | ip = 127.0.0.1; - | port = %d; - | %s - | reject_policy = negate; - | reject_recursively = on; - |} - | - |rr { - | name=localhost; - | reverse=on; - | a=127.0.0.1; - | owner=localhost; - | soa=localhost,root.localhost,42,86400,900,86400,86400; - |} - """.stripMargin - - def printToFile(f: java.io.File)(op: java.io.PrintWriter => Unit) { - val p = new java.io.PrintWriter(f) - try { - op(p) - } finally { - p.close() - } - } - - def refresh(context: Context) { - val holder = app.containerHolder - if (holder != null) holder.refresh() - } - - def getBlackList(context: Context): String = { - val default = context.getString(R.string.black_list) - try { - val container = app.containerHolder.getContainer - val update = container.getString("black_list") - if (update == null || update.isEmpty) default else update - } catch { - case ex: Exception => default - } - } - - def getPublicConfig(context: Context, container: Container, config: Config): Config = { - val url = container.getString("proxy_url") - val sig = Utils.getSignature(context) - val list = HttpRequest - .post(url) - .connectTimeout(2000) - .readTimeout(2000) - .send("sig="+sig) - .body - val proxies = util.Random.shuffle(list.split('|').toSeq) - val proxy = proxies.head.split(':') - - val host = proxy(0).trim - val port = proxy(1).trim.toInt - val password = proxy(2).trim - val method = proxy(3).trim - - new Config(config.isProxyApps, config.isBypassApps, config.isUdpDns, config.isAuth, config.isIpv6, - config.profileName, host, password, method, config.proxiedAppString, config.route, port, config.localPort, 0) - } - - def loadFromSharedPreferences = { - val isProxyApps = app.settings.getBoolean(Key.isProxyApps, false) - val isBypassApps = app.settings.getBoolean(Key.isBypassApps, false) - val isUdpDns = app.settings.getBoolean(Key.isUdpDns, false) - val isAuth = app.settings.getBoolean(Key.isAuth, false) - val isIpv6 = app.settings.getBoolean(Key.isIpv6, false) - - val profileName = app.settings.getString(Key.profileName, "default") - val proxy = app.settings.getString(Key.proxy, "127.0.0.1") - val sitekey = app.settings.getString(Key.sitekey, "default") - val encMethod = app.settings.getString(Key.encMethod, "table") - val route = app.settings.getString(Key.route, "all") - - val remotePort = app.settings.getInt(Key.remotePort, 1984) - val localPort = app.settings.getInt(Key.localPort, 1984) - val proxiedAppString = app.settings.getString(Key.proxied, "") - - val profileId = app.settings.getInt(Key.profileId, -1) - - new Config(isProxyApps, isBypassApps, isUdpDns, isAuth, isIpv6, profileName, proxy, sitekey, encMethod, - proxiedAppString, route, remotePort, localPort, profileId) - } -} diff --git a/src/main/scala/com/github/shadowsocks/utils/Constants.scala b/src/main/scala/com/github/shadowsocks/utils/Constants.scala index ec82513f..808dab55 100644 --- a/src/main/scala/com/github/shadowsocks/utils/Constants.scala +++ b/src/main/scala/com/github/shadowsocks/utils/Constants.scala @@ -47,6 +47,101 @@ object Executable { val TUN2SOCKS = "tun2socks" } +object ConfigUtils { + val SHADOWSOCKS = "{\"server\": \"%s\", \"server_port\": %d, \"local_port\": %d, \"password\": \"%s\", \"method\":\"%s\", \"timeout\": %d}" + val REDSOCKS = "base {\n" + + " log_debug = off;\n" + + " log_info = off;\n" + + " log = stderr;\n" + + " daemon = off;\n" + + " redirector = iptables;\n" + + "}\n" + + "redsocks {\n" + + " local_ip = 127.0.0.1;\n" + + " local_port = 8123;\n" + + " ip = 127.0.0.1;\n" + + " port = %d;\n" + + " type = socks5;\n" + + "}\n" + + val PDNSD_LOCAL = + """ + |global { + | perm_cache = 2048; + | cache_dir = "%s"; + | server_ip = %s; + | server_port = %d; + | query_method = tcp_only; + | run_ipv4 = on; + | min_ttl = 15m; + | max_ttl = 1w; + | timeout = 10; + | daemon = off; + |} + | + |server { + | label = "local"; + | ip = 127.0.0.1; + | port = %d; + | %s + | reject_policy = negate; + | reject_recursively = on; + | timeout = 5; + |} + | + |rr { + | name=localhost; + | reverse=on; + | a=127.0.0.1; + | owner=localhost; + | soa=localhost,root.localhost,42,86400,900,86400,86400; + |} + """.stripMargin + + val PDNSD_DIRECT = + """ + |global { + | perm_cache = 2048; + | cache_dir = "%s"; + | server_ip = %s; + | server_port = %d; + | query_method = tcp_only; + | run_ipv4 = on; + | min_ttl = 15m; + | max_ttl = 1w; + | timeout = 10; + | daemon = off; + |} + | + |server { + | label = "china-servers"; + | ip = 114.114.114.114, 112.124.47.27; + | timeout = 4; + | exclude = %s; + | policy = included; + | uptest = none; + | preset = on; + |} + | + |server { + | label = "local-server"; + | ip = 127.0.0.1; + | port = %d; + | %s + | reject_policy = negate; + | reject_recursively = on; + |} + | + |rr { + | name=localhost; + | reverse=on; + | a=127.0.0.1; + | owner=localhost; + | soa=localhost,root.localhost,42,86400,900,86400,86400; + |} + """.stripMargin +} + object Key { val profileId = "profileId" val profileName = "profileName" diff --git a/src/main/scala/com/github/shadowsocks/utils/Utils.scala b/src/main/scala/com/github/shadowsocks/utils/Utils.scala index 7305988a..c5c79121 100644 --- a/src/main/scala/com/github/shadowsocks/utils/Utils.scala +++ b/src/main/scala/com/github/shadowsocks/utils/Utils.scala @@ -120,6 +120,15 @@ object Utils { }) } + def printToFile(f: java.io.File)(op: java.io.PrintWriter => Unit) { + val p = new java.io.PrintWriter(f) + try { + op(p) + } finally { + p.close() + } + } + // Blocked > 3 seconds def toggleAirplaneMode(context: Context) = { if (Console.isRoot) {