Commit 4d27c8c0 authored by Max Lv's avatar Max Lv

fix #15

parent c11e4988
...@@ -50,11 +50,13 @@ ...@@ -50,11 +50,13 @@
<service <service
android:name=".ShadowsocksService" android:name=".ShadowsocksService"
android:enabled="true"/> android:process=":proxy"
android:exported="false"/>
<service <service
android:name=".ShadowVpnService" android:name=".ShadowVpnService"
android:label="@string/app_name" android:label="@string/app_name"
android:process=":vpn"
android:permission="android.permission.BIND_VPN_SERVICE" android:permission="android.permission.BIND_VPN_SERVICE"
android:exported="false"> android:exported="false">
<intent-filter> <intent-filter>
......
package com.github.shadowsocks;
interface IStateService {
int getState();
String getMessage();
}
...@@ -47,25 +47,15 @@ import android.support.v4.app.NotificationCompat ...@@ -47,25 +47,15 @@ import android.support.v4.app.NotificationCompat
import android.util.Log import android.util.Log
import com.google.analytics.tracking.android.EasyTracker import com.google.analytics.tracking.android.EasyTracker
import java.io._ import java.io._
import java.lang.ref.WeakReference
import android.net.VpnService import android.net.VpnService
import org.apache.http.conn.util.InetAddressUtils import org.apache.http.conn.util.InetAddressUtils
import android.os.Message import android.os.Message
import scala.Some import scala.Some
object ShadowVpnService { object ShadowVpnService {
def isServiceStarted: Boolean = { def isServiceStarted(context: Context): Boolean = {
if (sRunningInstance == null) { Utils.isServiceStarted("com.github.shadowsocks.ShadowVpnService", context)
false
} else if (sRunningInstance.get == null) {
sRunningInstance = null
false
} else {
true
}
} }
var sRunningInstance: WeakReference[ShadowVpnService] = null
} }
class ShadowVpnService extends VpnService { class ShadowVpnService extends VpnService {
...@@ -73,13 +63,12 @@ class ShadowVpnService extends VpnService { ...@@ -73,13 +63,12 @@ class ShadowVpnService extends VpnService {
val TAG = "ShadowVpnService" val TAG = "ShadowVpnService"
val BASE = "/data/data/com.github.shadowsocks/" val BASE = "/data/data/com.github.shadowsocks/"
val SHADOWSOCKS_CONF = "{\"server\": [%s], \"server_port\": %d, \"local_port\": %d, \"password\": %s, \"timeout\": %d}" val SHADOWSOCKS_CONF = "{\"server\": [%s], \"server_port\": %d, \"local_port\": %d, \"password\": %s, \"timeout\": %d}"
val MSG_CONNECT_START: Int = 0 val MSG_CONNECT_START = 0
val MSG_CONNECT_FINISH: Int = 1 val MSG_CONNECT_FINISH = 1
val MSG_CONNECT_SUCCESS: Int = 2 val MSG_CONNECT_SUCCESS = 2
val MSG_CONNECT_FAIL: Int = 3 val MSG_CONNECT_FAIL = 3
val MSG_HOST_CHANGE: Int = 4 val MSG_STOP_SELF = 5
val MSG_STOP_SELF: Int = 5 val MSG_VPN_ERROR = 6
val MSG_VPN_ERROR: Int = 6
val VPN_MTU = 1500 val VPN_MTU = 1500
...@@ -103,26 +92,39 @@ class ShadowVpnService extends VpnService { ...@@ -103,26 +92,39 @@ class ShadowVpnService extends VpnService {
var encMethod: String = null var encMethod: String = null
var apps: Array[ProxiedApp] = null var apps: Array[ProxiedApp] = null
private var state = State.INIT
private var message: String = null
def changeState(s: Int) {
changeState(s, null)
}
def changeState(s: Int, m: String) {
if (state != s) {
state = s
if (m != null) message = m
sendBroadcast(new Intent(Utils.ACTION_UPDATE_STATE))
}
}
val handler: Handler = new Handler { val handler: Handler = new Handler {
override def handleMessage(msg: Message) { override def handleMessage(msg: Message) {
val ed: SharedPreferences.Editor = settings.edit val ed: SharedPreferences.Editor = settings.edit
msg.what match { msg.what match {
case MSG_CONNECT_START => case MSG_CONNECT_START =>
ed.putBoolean("isConnecting", true) changeState(State.CONNECTING)
ed.remove("vpnError")
case MSG_CONNECT_FINISH =>
ed.putBoolean("isConnecting", false)
case MSG_CONNECT_SUCCESS => case MSG_CONNECT_SUCCESS =>
changeState(State.CONNECTED)
ed.putBoolean("isRunning", true) ed.putBoolean("isRunning", true)
case MSG_CONNECT_FAIL => case MSG_CONNECT_FAIL =>
changeState(State.FAILED)
ed.putBoolean("isRunning", false) ed.putBoolean("isRunning", false)
case MSG_HOST_CHANGE =>
ed.putString("appHost", appHost)
case MSG_VPN_ERROR => case MSG_VPN_ERROR =>
if (msg.obj != null) ed.putString("vpnError", msg.obj.asInstanceOf[String]) if (msg.obj != null) changeState(State.FAILED, msg.obj.asInstanceOf[String])
case MSG_STOP_SELF => case MSG_STOP_SELF =>
destroy() destroy()
stopSelf() stopSelf()
case _ =>
} }
ed.commit ed.commit
super.handleMessage(msg) super.handleMessage(msg)
...@@ -248,7 +250,6 @@ class ShadowVpnService extends VpnService { ...@@ -248,7 +250,6 @@ class ShadowVpnService extends VpnService {
handler.sendEmptyMessageDelayed(MSG_CONNECT_FINISH, 300) handler.sendEmptyMessageDelayed(MSG_CONNECT_FINISH, 300)
} }
}).start() }).start()
markServiceStarted()
} }
def waitForProcess(name: String): Boolean = { def waitForProcess(name: String): Boolean = {
...@@ -362,14 +363,6 @@ class ShadowVpnService extends VpnService { ...@@ -362,14 +363,6 @@ class ShadowVpnService extends VpnService {
notification.defaults |= Notification.DEFAULT_LIGHTS notification.defaults |= Notification.DEFAULT_LIGHTS
} }
def markServiceStarted() {
ShadowVpnService.sRunningInstance = new WeakReference[ShadowVpnService](this)
}
def markServiceStopped() {
ShadowVpnService.sRunningInstance = null
}
def notifyAlert(title: String, info: String) { def notifyAlert(title: String, info: String) {
val openIntent: Intent = new Intent(this, classOf[Shadowsocks]) val openIntent: Intent = new Intent(this, classOf[Shadowsocks])
openIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) openIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
...@@ -391,7 +384,16 @@ class ShadowVpnService extends VpnService { ...@@ -391,7 +384,16 @@ class ShadowVpnService extends VpnService {
if (VpnService.SERVICE_INTERFACE == action) { if (VpnService.SERVICE_INTERFACE == action) {
return super.onBind(intent) return super.onBind(intent)
} }
null
new IStateService.Stub {
def getMessage: String = {
val m = message
message = null
m
}
def getState: Int = state
}
} }
override def onCreate() { override def onCreate() {
...@@ -405,7 +407,7 @@ class ShadowVpnService extends VpnService { ...@@ -405,7 +407,7 @@ class ShadowVpnService extends VpnService {
// register close receiver // register close receiver
val filter = new IntentFilter() val filter = new IntentFilter()
filter.addAction(Intent.ACTION_SHUTDOWN) filter.addAction(Intent.ACTION_SHUTDOWN)
filter.addAction(Utils.CLOSE_ACTION) filter.addAction(Utils.ACTION_CLOSE)
receiver = new BroadcastReceiver { receiver = new BroadcastReceiver {
def onReceive(p1: Context, p2: Intent) { def onReceive(p1: Context, p2: Intent) {
destroy() destroy()
...@@ -416,6 +418,7 @@ class ShadowVpnService extends VpnService { ...@@ -416,6 +418,7 @@ class ShadowVpnService extends VpnService {
} }
def destroy() { def destroy() {
changeState(State.STOPPED)
EasyTracker.getTracker.sendEvent(TAG, "stop", getVersionName, 0L) EasyTracker.getTracker.sendEvent(TAG, "stop", getVersionName, 0L)
if (receiver != null) { if (receiver != null) {
unregisterReceiver(receiver) unregisterReceiver(receiver)
...@@ -428,22 +431,20 @@ class ShadowVpnService extends VpnService { ...@@ -428,22 +431,20 @@ class ShadowVpnService extends VpnService {
}.start() }.start()
val ed: SharedPreferences.Editor = settings.edit val ed: SharedPreferences.Editor = settings.edit
ed.putBoolean("isRunning", false) ed.putBoolean("isRunning", false)
ed.putBoolean("isConnecting", false)
ed.commit ed.commit
if (conn != null) { if (conn != null) {
conn.close() conn.close()
conn = null conn = null
} }
notificationManager.cancel(1) notificationManager.cancel(1)
markServiceStopped()
} }
/** Called when the activity is closed. */ /** Called when the activity is closed. */
override def onDestroy() { override def onDestroy() {
super.onDestroy()
EasyTracker.getTracker.setStartSession(false) EasyTracker.getTracker.setStartSession(false)
EasyTracker.getTracker.sendEvent(TAG, "stop", getVersionName, 0L) EasyTracker.getTracker.sendEvent(TAG, "stop", getVersionName, 0L)
destroy() destroy()
super.onDestroy()
} }
def killProcesses() { def killProcesses() {
......
...@@ -38,14 +38,10 @@ ...@@ -38,14 +38,10 @@
package com.github.shadowsocks package com.github.shadowsocks
import android.app.{Activity, AlertDialog, ProgressDialog} import android.app.{Activity, AlertDialog, ProgressDialog}
import android.content.Context import android.content._
import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.content.res.AssetManager import android.content.res.AssetManager
import android.graphics.Typeface import android.graphics.Typeface
import android.os.{Build, Bundle, Handler, Message} import android.os._
import android.preference.Preference import android.preference.Preference
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.util.Log import android.util.Log
...@@ -79,11 +75,17 @@ object Shadowsocks { ...@@ -79,11 +75,17 @@ object Shadowsocks {
val TAG = "Shadowsocks" val TAG = "Shadowsocks"
val REQUEST_CONNECT = 1 val REQUEST_CONNECT = 1
class ProxyFragment extends UnifiedPreferenceFragment with OnSharedPreferenceChangeListener { def isServiceStarted(context: Context): Boolean = {
ShadowsocksService.isServiceStarted(context) || ShadowVpnService.isServiceStarted(context)
}
class ProxyFragment extends UnifiedPreferenceFragment {
var receiver: BroadcastReceiver = null
private def setPreferenceEnabled() { private def setPreferenceEnabled() {
val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity) val state = getActivity.asInstanceOf[Shadowsocks].state
val enabled: Boolean = !settings.getBoolean("isRunning", false) && val enabled: Boolean = state != State.CONNECTED && state != State.CONNECTING
!settings.getBoolean("isConnecting", false)
for (name <- PROXY_PREFS) { for (name <- PROXY_PREFS) {
val pref: Preference = findPreference(name) val pref: Preference = findPreference(name)
if (pref != null) { if (pref != null) {
...@@ -92,32 +94,44 @@ object Shadowsocks { ...@@ -92,32 +94,44 @@ object Shadowsocks {
} }
} }
override def onStart() {
super.onStart()
val filter = new IntentFilter()
filter.addAction(Utils.ACTION_UPDATE_FRAGMENT)
receiver = new BroadcastReceiver {
def onReceive(p1: Context, p2: Intent) {
setPreferenceEnabled()
}
}
getActivity.getApplicationContext.registerReceiver(receiver, filter)
}
override def onStop() {
super.onStop()
getActivity.getApplicationContext.unregisterReceiver(receiver)
}
override def onResume() { override def onResume() {
super.onResume() super.onResume()
setPreferenceEnabled() setPreferenceEnabled()
getPreferenceScreen.getSharedPreferences.registerOnSharedPreferenceChangeListener(this)
} }
override def onPause() { override def onPause() {
super.onPause() super.onPause()
getPreferenceScreen.getSharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
}
def onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
if ((key == "isRunning") || (key == "isGlobalProxy")) {
setPreferenceEnabled()
}
} }
} }
class FeatureFragment extends UnifiedPreferenceFragment with OnSharedPreferenceChangeListener { class FeatureFragment extends UnifiedPreferenceFragment {
var receiver: BroadcastReceiver = null
private def setPreferenceEnabled() { private def setPreferenceEnabled() {
val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity) val state = getActivity.asInstanceOf[Shadowsocks].state
val enabled: Boolean = !settings.getBoolean("isRunning", false) && val enabled: Boolean = state != State.CONNECTED && state != State.CONNECTING
!settings.getBoolean("isConnecting", false)
for (name <- Shadowsocks.FEATRUE_PREFS) { for (name <- Shadowsocks.FEATRUE_PREFS) {
val pref: Preference = findPreference(name) val pref: Preference = findPreference(name)
if (pref != null) { if (pref != null) {
val settings = PreferenceManager.getDefaultSharedPreferences(getActivity)
if ((name == "isBypassApps") || (name == "proxyedApps")) { if ((name == "isBypassApps") || (name == "proxyedApps")) {
val isGlobalProxy: Boolean = settings.getBoolean("isGlobalProxy", false) val isGlobalProxy: Boolean = settings.getBoolean("isGlobalProxy", false)
pref.setEnabled(enabled && !isGlobalProxy) pref.setEnabled(enabled && !isGlobalProxy)
...@@ -130,21 +144,30 @@ object Shadowsocks { ...@@ -130,21 +144,30 @@ object Shadowsocks {
} }
} }
override def onStart() {
super.onStart()
val filter = new IntentFilter()
filter.addAction(Utils.ACTION_UPDATE_FRAGMENT)
receiver = new BroadcastReceiver {
def onReceive(p1: Context, p2: Intent) {
setPreferenceEnabled()
}
}
getActivity.getApplicationContext.registerReceiver(receiver, filter)
}
override def onStop() {
super.onStop()
getActivity.getApplicationContext.unregisterReceiver(receiver)
}
override def onResume() { override def onResume() {
super.onResume() super.onResume()
setPreferenceEnabled() setPreferenceEnabled()
getPreferenceScreen.getSharedPreferences.registerOnSharedPreferenceChangeListener(this)
} }
override def onPause() { override def onPause() {
super.onPause() super.onPause()
getPreferenceScreen.getSharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
}
def onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
if ((key == "isRunning") || (key == "isGlobalProxy")) {
setPreferenceEnabled()
}
} }
} }
...@@ -173,20 +196,34 @@ object Typefaces { ...@@ -173,20 +196,34 @@ object Typefaces {
} }
class Shadowsocks class Shadowsocks
extends UnifiedSherlockPreferenceActivity with CompoundButton.OnCheckedChangeListener with extends UnifiedSherlockPreferenceActivity
OnSharedPreferenceChangeListener { with CompoundButton.OnCheckedChangeListener {
private val MSG_CRASH_RECOVER: Int = 1 private val MSG_CRASH_RECOVER: Int = 1
private val MSG_INITIAL_FINISH: Int = 2 private val MSG_INITIAL_FINISH: Int = 2
private var switchButton: Switch = null private var switchButton: Switch = null
private var progressDialog: ProgressDialog = null private var progressDialog: ProgressDialog = null
private var settings: SharedPreferences = null
private var prepared = false private var prepared = false
private var state = State.INIT
private var binder: IStateService = null
private var receiver: StateBroadcastReceiver = null
private val connection = new ServiceConnection {
def onServiceDisconnected(name: ComponentName) {
onStateChanged(binder.getState, binder.getMessage)
binder = null
}
def onServiceConnected(name: ComponentName, s: IBinder) {
binder = IStateService.Stub.asInterface(s)
onStateChanged(binder.getState, binder.getMessage)
}
}
private val handler: Handler = new Handler { private val handler: Handler = new Handler {
override def handleMessage(msg: Message) { override def handleMessage(msg: Message) {
val settings: SharedPreferences = PreferenceManager
.getDefaultSharedPreferences(Shadowsocks.this)
val ed: SharedPreferences.Editor = settings.edit val ed: SharedPreferences.Editor = settings.edit
msg.what match { msg.what match {
case MSG_CRASH_RECOVER => case MSG_CRASH_RECOVER =>
...@@ -326,13 +363,14 @@ class Shadowsocks ...@@ -326,13 +363,14 @@ class Shadowsocks
getSupportActionBar.setDisplayShowTitleEnabled(false) getSupportActionBar.setDisplayShowTitleEnabled(false)
getSupportActionBar.setDisplayShowCustomEnabled(true) getSupportActionBar.setDisplayShowCustomEnabled(true)
getSupportActionBar.setDisplayShowHomeEnabled(false) getSupportActionBar.setDisplayShowHomeEnabled(false)
switchButton = switchLayout.findViewById(R.id.switchButton).asInstanceOf[Switch] switchButton = switchLayout.findViewById(R.id.switchButton).asInstanceOf[Switch]
val title: TextView = switchLayout.findViewById(R.id.title).asInstanceOf[TextView] val title: TextView = switchLayout.findViewById(R.id.title).asInstanceOf[TextView]
val tf: Typeface = Typefaces.get(this, "fonts/Iceland.ttf") val tf: Typeface = Typefaces.get(this, "fonts/Iceland.ttf")
if (tf != null) title.setTypeface(tf) if (tf != null) title.setTypeface(tf)
title.setText(R.string.app_name) title.setText(R.string.app_name)
val settings: SharedPreferences = PreferenceManager settings = PreferenceManager.getDefaultSharedPreferences(this)
.getDefaultSharedPreferences(Shadowsocks.this)
val init: Boolean = !settings.getBoolean("isRunning", false) && val init: Boolean = !settings.getBoolean("isRunning", false) &&
!settings.getBoolean("isConnecting", false) !settings.getBoolean("isConnecting", false)
if (init) { if (init) {
...@@ -412,29 +450,30 @@ class Shadowsocks ...@@ -412,29 +450,30 @@ class Shadowsocks
protected override def onPause() { protected override def onPause() {
super.onPause() super.onPause()
prepared = false prepared = false
PreferenceManager
.getDefaultSharedPreferences(this)
.unregisterOnSharedPreferenceChangeListener(this)
} }
protected override def onResume() { protected override def onResume() {
super.onResume() super.onResume()
if (!prepared) { if (!prepared) {
val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) if (Shadowsocks.isServiceStarted(this)) {
if (isServiceStarted) {
switchButton.setChecked(true) switchButton.setChecked(true)
if (ShadowVpnService.isServiceStarted) { if (ShadowVpnService.isServiceStarted(this)) {
val style = new Style.Builder() val style = new Style.Builder()
.setBackgroundColorValue(Style.holoBlueLight) .setBackgroundColorValue(Style.holoBlueLight)
.setDuration(Style.DURATION_INFINITE) .setDuration(Style.DURATION_INFINITE)
.build() .build()
switchButton.setEnabled(false) switchButton.setEnabled(false)
Crouton.makeText(Shadowsocks.this, R.string.vpn_status, style).show() Crouton.makeText(Shadowsocks.this, R.string.vpn_status, style).show()
if (binder == null) bindService(new Intent(this, classOf[ShadowVpnService]), connection, 0)
} else {
if (binder == null) bindService(new Intent(this, classOf[ShadowsocksService]), connection, 0)
} }
setPreferenceEnabled(false)
} else { } else {
switchButton.setEnabled(true) switchButton.setEnabled(true)
switchButton.setChecked(false) switchButton.setChecked(false)
Crouton.cancelAllCroutons() Crouton.cancelAllCroutons()
setPreferenceEnabled(true)
if (settings.getBoolean("isRunning", false)) { if (settings.getBoolean("isRunning", false)) {
new Thread { new Thread {
override def run() { override def run() {
...@@ -445,18 +484,11 @@ class Shadowsocks ...@@ -445,18 +484,11 @@ class Shadowsocks
} }
} }
} }
setPreferenceEnabled()
switchButton.setOnCheckedChangeListener(this) switchButton.setOnCheckedChangeListener(this)
PreferenceManager
.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this)
} }
private def setPreferenceEnabled() { private def setPreferenceEnabled(enabled: Boolean) {
val settings: SharedPreferences = PreferenceManager
.getDefaultSharedPreferences(Shadowsocks.this)
val enabled: Boolean = !settings.getBoolean("isRunning", false) &&
!settings.getBoolean("isConnecting", false)
for (name <- Shadowsocks.PROXY_PREFS) { for (name <- Shadowsocks.PROXY_PREFS) {
val pref: Preference = findPreference(name) val pref: Preference = findPreference(name)
if (pref != null) { if (pref != null) {
...@@ -478,54 +510,16 @@ class Shadowsocks ...@@ -478,54 +510,16 @@ class Shadowsocks
} }
} }
def onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
if ((key == "isConnecting" || key == "isRunning")
&& !settings.getBoolean("isConnecting", false)) {
if (settings.getBoolean("isRunning", false)) {
if (!switchButton.isChecked) switchButton.setChecked(true)
} else {
if (switchButton.isChecked) {
switchButton.setEnabled(true)
switchButton.setChecked(false)
Crouton.cancelAllCroutons()
}
val msg = settings.getString("vpnError", null)
if (msg != null) {
Crouton.cancelAllCroutons()
val style = new Style.Builder()
.setBackgroundColorValue(Style.holoRedLight)
.setDuration(Style.DURATION_INFINITE)
.build()
Crouton.makeText(this, getString(R.string.vpn_error).format(msg), style).show()
}
}
}
if (key == "isConnecting") {
if (settings.getBoolean("isConnecting", false)) {
if (progressDialog == null) {
progressDialog = ProgressDialog.show(this, "", getString(R.string.connecting), true, true)
}
} else {
if (progressDialog != null) {
progressDialog.dismiss()
progressDialog = null
}
}
}
if ((key == "isRunning") || (key == "isGlobalProxy")) {
setPreferenceEnabled()
}
}
override def onStart() { override def onStart() {
super.onStart() super.onStart()
receiver = new StateBroadcastReceiver()
registerReceiver(receiver, new IntentFilter(Utils.ACTION_UPDATE_STATE))
EasyTracker.getInstance.activityStart(this) EasyTracker.getInstance.activityStart(this)
} }
override def onStop() { override def onStop() {
super.onStop() super.onStop()
unregisterReceiver(receiver)
EasyTracker.getInstance.activityStop(this) EasyTracker.getInstance.activityStop(this)
if (progressDialog != null) { if (progressDialog != null) {
progressDialog.dismiss() progressDialog.dismiss()
...@@ -536,6 +530,7 @@ class Shadowsocks ...@@ -536,6 +530,7 @@ class Shadowsocks
override def onDestroy() { override def onDestroy() {
super.onDestroy() super.onDestroy()
Crouton.cancelAllCroutons() Crouton.cancelAllCroutons()
if (binder != null) unbindService(connection)
} }
def reset() { def reset() {
...@@ -589,23 +584,18 @@ class Shadowsocks ...@@ -589,23 +584,18 @@ class Shadowsocks
Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && !Utils.getRoot Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && !Utils.getRoot
} }
def isServiceStarted: Boolean = {
ShadowsocksService.isServiceStarted || ShadowVpnService.isServiceStarted
}
def serviceStop() { def serviceStop() {
if (ShadowVpnService.isServiceStarted) { if (ShadowVpnService.isServiceStarted(this)) {
stopService(new Intent(this, classOf[ShadowVpnService])) stopService(new Intent(this, classOf[ShadowVpnService]))
} }
if (ShadowsocksService.isServiceStarted) { if (ShadowsocksService.isServiceStarted(this)) {
sendBroadcast(new Intent(Utils.CLOSE_ACTION)) sendBroadcast(new Intent(Utils.ACTION_CLOSE))
} }
} }
/** Called when connect button is clicked. */ /** Called when connect button is clicked. */
def serviceStart: Boolean = { def serviceStart: Boolean = {
val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
val proxy = settings.getString("proxy", "") val proxy = settings.getString("proxy", "")
if (isTextEmpty(proxy, getString(R.string.proxy_empty))) return false if (isTextEmpty(proxy, getString(R.string.proxy_empty))) return false
val portText = settings.getString("port", "") val portText = settings.getString("port", "")
...@@ -624,9 +614,10 @@ class Shadowsocks ...@@ -624,9 +614,10 @@ class Shadowsocks
} }
if (isVpnEnabled) { if (isVpnEnabled) {
if (ShadowVpnService.isServiceStarted) return false if (ShadowVpnService.isServiceStarted(this)) return false
val it: Intent = new Intent(this, classOf[ShadowVpnService]) val it: Intent = new Intent(this, classOf[ShadowVpnService])
startService(it) startService(it)
bindService(it, connection, 0)
val style = new Style.Builder() val style = new Style.Builder()
.setBackgroundColorValue(Style.holoBlueLight) .setBackgroundColorValue(Style.holoBlueLight)
.setDuration(Style.DURATION_INFINITE) .setDuration(Style.DURATION_INFINITE)
...@@ -634,11 +625,11 @@ class Shadowsocks ...@@ -634,11 +625,11 @@ class Shadowsocks
Crouton.makeText(Shadowsocks.this, R.string.vpn_status, style).show() Crouton.makeText(Shadowsocks.this, R.string.vpn_status, style).show()
switchButton.setEnabled(false) switchButton.setEnabled(false)
} else { } else {
if (ShadowsocksService.isServiceStarted) return false if (ShadowsocksService.isServiceStarted(this)) return false
val it: Intent = new Intent(this, classOf[ShadowsocksService]) val it: Intent = new Intent(this, classOf[ShadowsocksService])
startService(it) startService(it)
bindService(it, connection, 0)
} }
true true
} }
...@@ -688,4 +679,64 @@ class Shadowsocks ...@@ -688,4 +679,64 @@ class Shadowsocks
val alert: AlertDialog = builder.create val alert: AlertDialog = builder.create
alert.show() alert.show()
} }
def clearDialog() {
if (progressDialog != null) {
progressDialog.dismiss()
progressDialog = null
}
}
def onStateChanged(s: Int, m: String) {
if (state != s) {
state = s
state match {
case State.CONNECTING => {
if (progressDialog == null) {
progressDialog = ProgressDialog.show(Shadowsocks.this, "", getString(R.string.connecting), true, true)
}
setPreferenceEnabled(false)
}
case State.CONNECTED => {
clearDialog()
if (!switchButton.isChecked) switchButton.setChecked(true)
setPreferenceEnabled(false)
}
case State.FAILED => {
clearDialog()
if (switchButton.isChecked) {
switchButton.setEnabled(true)
switchButton.setChecked(false)
Crouton.cancelAllCroutons()
}
if (m != null) {
Crouton.cancelAllCroutons()
val style = new Style.Builder()
.setBackgroundColorValue(Style.holoRedLight)
.setDuration(Style.DURATION_INFINITE)
.build()
Crouton.makeText(Shadowsocks.this, getString(R.string.vpn_error).format(m), style).show()
}
setPreferenceEnabled(true)
}
case State.STOPPED => {
clearDialog()
Crouton.cancelAllCroutons()
if (switchButton.isChecked) switchButton.setChecked(false)
setPreferenceEnabled(true)
}
}
sendBroadcast(new Intent(Utils.ACTION_UPDATE_FRAGMENT))
}
}
class StateBroadcastReceiver extends BroadcastReceiver {
override def onReceive(context: Context, intent: Intent) {
if (binder != null) {
val s = binder.getState
val m = binder.getMessage
onStateChanged(s, m)
}
}
}
} }
...@@ -77,7 +77,7 @@ class ShadowsocksReceiver extends BroadcastReceiver { ...@@ -77,7 +77,7 @@ class ShadowsocksReceiver extends BroadcastReceiver {
} }
if (Utils.getRoot) { if (Utils.getRoot) {
if (ShadowsocksService.isServiceStarted) return if (ShadowsocksService.isServiceStarted(context)) return
val it: Intent = new Intent(context, classOf[ShadowsocksService]) val it: Intent = new Intent(context, classOf[ShadowsocksService])
context.startService(it) context.startService(it)
} }
......
...@@ -44,10 +44,7 @@ import android.app.Service ...@@ -44,10 +44,7 @@ import android.app.Service
import android.content._ import android.content._
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Handler import android.os._
import android.os.IBinder
import android.os.Message
import android.os.PowerManager
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.support.v4.app.NotificationCompat import android.support.v4.app.NotificationCompat
import android.util.Log import android.util.Log
...@@ -56,7 +53,6 @@ import java.io.BufferedReader ...@@ -56,7 +53,6 @@ import java.io.BufferedReader
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.io.FileReader import java.io.FileReader
import java.io.IOException import java.io.IOException
import java.lang.ref.WeakReference
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method import java.lang.reflect.Method
import org.apache.http.conn.util.InetAddressUtils import org.apache.http.conn.util.InetAddressUtils
...@@ -64,18 +60,9 @@ import scala.collection._ ...@@ -64,18 +60,9 @@ import scala.collection._
import scala.Some import scala.Some
object ShadowsocksService { object ShadowsocksService {
def isServiceStarted: Boolean = { def isServiceStarted(context: Context): Boolean = {
if (sRunningInstance == null) { Utils.isServiceStarted("com.github.shadowsocks.ShadowsocksService", context)
false
} else if (sRunningInstance.get == null) {
sRunningInstance = null
false
} else {
true
}
} }
var sRunningInstance: WeakReference[ShadowsocksService] = null
} }
class ShadowsocksService extends Service { class ShadowsocksService extends Service {
...@@ -101,13 +88,14 @@ class ShadowsocksService extends Service { ...@@ -101,13 +88,14 @@ class ShadowsocksService extends Service {
val CMD_IPTABLES_REDIRECT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " + "-j REDIRECT --to 8123\n" val CMD_IPTABLES_REDIRECT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " + "-j REDIRECT --to 8123\n"
val CMD_IPTABLES_DNAT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " + val CMD_IPTABLES_DNAT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " +
"-j DNAT --to-destination 127.0.0.1:8123\n" "-j DNAT --to-destination 127.0.0.1:8123\n"
val MSG_CONNECT_START: Int = 0 val DNS_PORT = 8153
val MSG_CONNECT_FINISH: Int = 1
val MSG_CONNECT_SUCCESS: Int = 2 val MSG_CONNECT_START = 0
val MSG_CONNECT_FAIL: Int = 3 val MSG_CONNECT_FINISH = 1
val MSG_HOST_CHANGE: Int = 4 val MSG_CONNECT_SUCCESS = 2
val MSG_STOP_SELF: Int = 5 val MSG_CONNECT_FAIL = 3
val DNS_PORT: Int = 8153 val MSG_STOP_SELF = 4
val MSG_VPN_ERROR = 5
val mStartForegroundSignature = Array[Class[_]](classOf[Int], classOf[Notification]) val mStartForegroundSignature = Array[Class[_]](classOf[Int], classOf[Notification])
val mStopForegroundSignature = Array[Class[_]](classOf[Boolean]) val mStopForegroundSignature = Array[Class[_]](classOf[Boolean])
...@@ -115,7 +103,6 @@ class ShadowsocksService extends Service { ...@@ -115,7 +103,6 @@ class ShadowsocksService extends Service {
var receiver: BroadcastReceiver = null var receiver: BroadcastReceiver = null
var notificationManager: NotificationManager = null var notificationManager: NotificationManager = null
var mWakeLock: PowerManager#WakeLock = null
var appHost: String = null var appHost: String = null
var remotePort: Int = 0 var remotePort: Int = 0
var localPort: Int = 0 var localPort: Int = 0
...@@ -136,6 +123,36 @@ class ShadowsocksService extends Service { ...@@ -136,6 +123,36 @@ class ShadowsocksService extends Service {
var mStartForegroundArgs = new Array[AnyRef](2) var mStartForegroundArgs = new Array[AnyRef](2)
var mStopForegroundArgs = new Array[AnyRef](1) var mStopForegroundArgs = new Array[AnyRef](1)
private var state = State.INIT
private def changeState(s: Int) {
if (state != s) {
state = s
sendBroadcast(new Intent(Utils.ACTION_UPDATE_STATE))
}
}
val handler: Handler = new Handler {
override def handleMessage(msg: Message) {
val ed: SharedPreferences.Editor = settings.edit
msg.what match {
case MSG_CONNECT_START =>
changeState(State.CONNECTING)
case MSG_CONNECT_SUCCESS =>
changeState(State.CONNECTED)
ed.putBoolean("isRunning", true)
case MSG_CONNECT_FAIL =>
changeState(State.FAILED)
ed.putBoolean("isRunning", false)
case MSG_STOP_SELF =>
stopSelf()
case _ =>
}
ed.commit
super.handleMessage(msg)
}
}
def getPid(name: String): Int = { def getPid(name: String): Int = {
try { try {
val reader: BufferedReader = new BufferedReader(new FileReader(BASE + name + ".pid")) val reader: BufferedReader = new BufferedReader(new FileReader(BASE + name + ".pid"))
...@@ -263,7 +280,6 @@ class ShadowsocksService extends Service { ...@@ -263,7 +280,6 @@ class ShadowsocksService extends Service {
handler.sendEmptyMessageDelayed(MSG_CONNECT_FINISH, 500) handler.sendEmptyMessageDelayed(MSG_CONNECT_FINISH, 500)
} }
}).start() }).start()
markServiceStarted()
} }
def startRedsocksDaemon() { def startRedsocksDaemon() {
...@@ -321,19 +337,11 @@ class ShadowsocksService extends Service { ...@@ -321,19 +337,11 @@ class ShadowsocksService extends Service {
} }
} }
def markServiceStarted() {
ShadowsocksService.sRunningInstance = new WeakReference[ShadowsocksService](this)
}
def markServiceStopped() {
ShadowsocksService.sRunningInstance = null
}
def notifyForegroundAlert(title: String, info: String) { def notifyForegroundAlert(title: String, info: String) {
val openIntent: Intent = new Intent(this, classOf[Shadowsocks]) val openIntent: Intent = new Intent(this, classOf[Shadowsocks])
openIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) openIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val contentIntent: PendingIntent = PendingIntent.getActivity(this, 0, openIntent, 0) val contentIntent: PendingIntent = PendingIntent.getActivity(this, 0, openIntent, 0)
val closeIntent: Intent = new Intent(Utils.CLOSE_ACTION) val closeIntent: Intent = new Intent(Utils.ACTION_CLOSE)
val actionIntent: PendingIntent = PendingIntent.getBroadcast(this, 0, closeIntent, 0) val actionIntent: PendingIntent = PendingIntent.getBroadcast(this, 0, closeIntent, 0)
val builder: NotificationCompat.Builder = new NotificationCompat.Builder(this) val builder: NotificationCompat.Builder = new NotificationCompat.Builder(this)
builder builder
...@@ -365,7 +373,10 @@ class ShadowsocksService extends Service { ...@@ -365,7 +373,10 @@ class ShadowsocksService extends Service {
} }
def onBind(intent: Intent): IBinder = { def onBind(intent: Intent): IBinder = {
null new IStateService.Stub {
def getMessage: String = null
def getState: Int = state
}
} }
override def onCreate() { override def onCreate() {
...@@ -399,7 +410,7 @@ class ShadowsocksService extends Service { ...@@ -399,7 +410,7 @@ class ShadowsocksService extends Service {
// register close receiver // register close receiver
val filter = new IntentFilter() val filter = new IntentFilter()
filter.addAction(Intent.ACTION_SHUTDOWN) filter.addAction(Intent.ACTION_SHUTDOWN)
filter.addAction(Utils.CLOSE_ACTION) filter.addAction(Utils.ACTION_CLOSE)
receiver = new BroadcastReceiver() { receiver = new BroadcastReceiver() {
def onReceive(p1: Context, p2: Intent) { def onReceive(p1: Context, p2: Intent) {
stopSelf() stopSelf()
...@@ -410,7 +421,7 @@ class ShadowsocksService extends Service { ...@@ -410,7 +421,7 @@ class ShadowsocksService extends Service {
/** Called when the activity is closed. */ /** Called when the activity is closed. */
override def onDestroy() { override def onDestroy() {
super.onDestroy() changeState(State.STOPPED)
EasyTracker.getTracker.setStartSession(false) EasyTracker.getTracker.setStartSession(false)
EasyTracker.getTracker.sendEvent(TAG, "stop", getVersionName, 0L) EasyTracker.getTracker.sendEvent(TAG, "stop", getVersionName, 0L)
stopForegroundCompat(1) stopForegroundCompat(1)
...@@ -421,13 +432,12 @@ class ShadowsocksService extends Service { ...@@ -421,13 +432,12 @@ class ShadowsocksService extends Service {
}.start() }.start()
val ed: SharedPreferences.Editor = settings.edit val ed: SharedPreferences.Editor = settings.edit
ed.putBoolean("isRunning", false) ed.putBoolean("isRunning", false)
ed.putBoolean("isConnecting", false)
ed.commit ed.commit
markServiceStopped()
if (receiver != null) { if (receiver != null) {
unregisterReceiver(receiver) unregisterReceiver(receiver)
receiver = null receiver = null
} }
super.onDestroy()
} }
def killProcesses() { def killProcesses() {
...@@ -572,31 +582,5 @@ class ShadowsocksService extends Service { ...@@ -572,31 +582,5 @@ class ShadowsocksService extends Service {
invokeMethod(mSetForeground, mSetForegroundArgs) invokeMethod(mSetForeground, mSetForegroundArgs)
} }
val handler: Handler = new Handler {
override def handleMessage(msg: Message) {
val ed: SharedPreferences.Editor = settings.edit
msg.what match {
case MSG_CONNECT_START =>
ed.putBoolean("isConnecting", true)
val pm: PowerManager = getSystemService(Context.POWER_SERVICE).asInstanceOf[PowerManager]
mWakeLock = pm
.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,
"GAEProxy")
mWakeLock.acquire()
case MSG_CONNECT_FINISH =>
ed.putBoolean("isConnecting", false)
if (mWakeLock != null && mWakeLock.isHeld) mWakeLock.release()
case MSG_CONNECT_SUCCESS =>
ed.putBoolean("isRunning", true)
case MSG_CONNECT_FAIL =>
ed.putBoolean("isRunning", false)
case MSG_HOST_CHANGE =>
ed.putString("appHost", appHost)
case MSG_STOP_SELF =>
stopSelf()
}
ed.commit
super.handleMessage(msg)
}
}
} }
...@@ -50,10 +50,22 @@ import scala.collection.mutable.ArrayBuffer ...@@ -50,10 +50,22 @@ import scala.collection.mutable.ArrayBuffer
import org.xbill.DNS._ import org.xbill.DNS._
import scala.Some import scala.Some
import android.graphics.{Canvas, Bitmap} import android.graphics.{Canvas, Bitmap}
import android.app.ActivityManager
object State {
val INIT = 0
val CONNECTED = 1
val CONNECTING = 2
val FAILED = 3
val STOPPED = 4
}
object Utils { object Utils {
val CLOSE_ACTION = "com.github.shadowsocks.ACTION_SHUTDOWN" val ACTION_CLOSE = "com.github.shadowsocks.ACTION_SHUTDOWN"
val ACTION_UPDATE_STATE = "com.github.shadowsocks.ACTION_UPDATE_STATE"
val ACTION_UPDATE_FRAGMENT = "com.github.shadowsocks.ACTION_UPDATE_FRAGMENT"
val TAG: String = "Shadowsocks" val TAG: String = "Shadowsocks"
val ABI_PROP: String = "ro.product.cpu.abi" val ABI_PROP: String = "ro.product.cpu.abi"
val ABI2_PROP: String = "ro.product.cpu.abi2" val ABI2_PROP: String = "ro.product.cpu.abi2"
...@@ -75,6 +87,21 @@ object Utils { ...@@ -75,6 +87,21 @@ object Utils {
var data_path: String = null var data_path: String = null
var rootTries = 0 var rootTries = 0
def isServiceStarted(name: String, context: Context): Boolean = {
import scala.collection.JavaConversions._
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE).asInstanceOf[ActivityManager]
val services = activityManager.getRunningServices(Integer.MAX_VALUE)
for (service <- services) {
if (service.service.getClassName == name) {
return true
}
}
false
}
def resolve(host: String, addrType: Int): Option[String] = { def resolve(host: String, addrType: Int): Option[String] = {
val lookup = new Lookup(host, addrType) val lookup = new Lookup(host, addrType)
val resolver = new SimpleResolver("8.8.8.8") val resolver = new SimpleResolver("8.8.8.8")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment