Remove .aidl.Config and persisting SharedPreferences (#742)
Previously a profile can exist in the following forms: * `.database.Profile` used for everything backend; * `.ShadowsocksSettings` which uses Preferences to provide UI to tweak settings; * `.aidl.Config` used for IPC; * `<data dir>/shared_pref/com.github.shadowsocks_preferences.xml` which exists for no apparent reason. Thus long code block can be seen whose purpose is simply converting the data from one form to another. This commit intends to remove the latter two by: 1. Using profile ID and take advantage of fs r/w lock SQLite uses for IPC and better extensibility; 2. Stop persisting redundant fields in the preferences file and update database directly.
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
package com.github.shadowsocks.aidl;
|
||||
|
||||
parcelable Config;
|
||||
@@ -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<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 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -7,36 +7,43 @@
|
||||
|
||||
<com.github.shadowsocks.preferences.SummaryEditTextPreference
|
||||
android:key="profileName"
|
||||
android:persistent="false"
|
||||
android:title="@string/profile_name"/>
|
||||
<com.github.shadowsocks.preferences.SummaryEditTextPreference
|
||||
android:key="proxy"
|
||||
android:persistent="false"
|
||||
android:summary="@string/proxy_summary"
|
||||
android:title="@string/proxy"/>
|
||||
<com.github.shadowsocks.preferences.NumberPickerPreference
|
||||
app:min="1"
|
||||
app:max="65535"
|
||||
android:key="remotePortNum"
|
||||
android:persistent="false"
|
||||
android:summary="@string/remote_port_summary"
|
||||
android:title="@string/remote_port"/>
|
||||
<com.github.shadowsocks.preferences.NumberPickerPreference
|
||||
app:min="1025"
|
||||
app:max="65535"
|
||||
android:key="localPortNum"
|
||||
android:persistent="false"
|
||||
android:summary="@string/port_summary"
|
||||
android:title="@string/port"/>
|
||||
<com.github.shadowsocks.preferences.PasswordEditTextPreference
|
||||
android:inputType="textPassword"
|
||||
android:key="sitekey"
|
||||
android:persistent="false"
|
||||
android:summary="@string/sitekey_summary"
|
||||
android:title="@string/sitekey"/>
|
||||
<com.github.shadowsocks.preferences.DropDownPreference
|
||||
android:key="encMethod"
|
||||
android:persistent="false"
|
||||
app:entries="@array/enc_method_entry"
|
||||
app:entryValues="@array/enc_method_value"
|
||||
android:summary="%s"
|
||||
android:title="@string/enc_method"/>
|
||||
<SwitchPreference
|
||||
android:key="isAuth"
|
||||
android:persistent="false"
|
||||
android:summary="@string/onetime_auth_summary"
|
||||
android:title="@string/onetime_auth"/>
|
||||
|
||||
@@ -47,20 +54,24 @@
|
||||
|
||||
<com.github.shadowsocks.preferences.DropDownPreference
|
||||
android:key="route"
|
||||
android:persistent="false"
|
||||
app:entries="@array/route_entry"
|
||||
app:entryValues="@array/route_value"
|
||||
android:summary="%s"
|
||||
android:title="@string/route_list"/>
|
||||
<SwitchPreference
|
||||
android:key="isIpv6"
|
||||
android:persistent="false"
|
||||
android:summary="@string/ipv6_summary"
|
||||
android:title="@string/ipv6"/>
|
||||
<SwitchPreference
|
||||
android:key="isProxyApps"
|
||||
android:persistent="false"
|
||||
android:summary="@string/proxied_apps_summary"
|
||||
android:title="@string/proxied_apps"/>
|
||||
<SwitchPreference
|
||||
android:key="isUdpDns"
|
||||
android:persistent="false"
|
||||
android:summary="@string/udp_dns_summary"
|
||||
android:title="@string/udp_dns"/>
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
/*
|
||||
* Shadowsocks - A shadowsocks client for Android
|
||||
* Copyright (C) 2014 <max.c.lv@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* ___====-_ _-====___
|
||||
* _--^^^#####// \\#####^^^--_
|
||||
* _-^##########// ( ) \\##########^-_
|
||||
* -############// |\^^/| \\############-
|
||||
* _/############// (@::@) \\############\_
|
||||
* /#############(( \\// ))#############\
|
||||
* -###############\\ (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)
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user