diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index bb48c119..d90311cc 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -1,102 +1,107 @@
+ package="com.github.shadowsocks"
+ android:versionCode="120"
+ android:versionName="2.8.3">
-
-
-
-
+
+
+
+
-
+
-
+
-
+
-
-
+
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
-
+
+
+
+
+
+
+
+
diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala
index 7a5a021a..6c2a3532 100644
--- a/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala
+++ b/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala
@@ -65,10 +65,15 @@ class ShadowsocksReceiver extends BroadcastReceiver {
val isAutoConnect: Boolean = settings.getBoolean(Key.isAutoConnect, false)
val isInstalled: Boolean = status.getBoolean(versionName, false)
if (isAutoConnect && isInstalled) {
- val intent = new Intent(context, classOf[ShadowsocksRunnerActivity])
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
- context.startActivity(intent)
+ if (Utils.isLollipopOrAbove) {
+ val intent = new Intent(context, classOf[ShadowsocksRunnerService])
+ context.startService(intent)
+ } else {
+ val intent = new Intent(context, classOf[ShadowsocksRunnerActivity])
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ context.startActivity(intent)
+ }
}
}
}
diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerService.scala
new file mode 100644
index 00000000..981c5d30
--- /dev/null
+++ b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerService.scala
@@ -0,0 +1,125 @@
+/*
+ * Shadowsocks - A shadowsocks client for Android
+ * Copyright (C) 2014
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ *
+ * ___====-_ _-====___
+ * _--^^^#####// \\#####^^^--_
+ * _-^##########// ( ) \\##########^-_
+ * -############// |\^^/| \\############-
+ * _/############// (@::@) \\############\_
+ * /#############(( \\// ))#############\
+ * -###############\\ (oo) //###############-
+ * -#################\\ / VV \ //#################-
+ * -###################\\/ \//###################-
+ * _#/|##########/\######( /\ )######/\##########|\#_
+ * |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \|
+ * ` |/ V V ` V \#\| | | |/#/ V ' V V \| '
+ * ` ` ` ` / | | | | \ ' ' ' '
+ * ( | | | | )
+ * __\ | | | | /__
+ * (vvv(VVV)(VVV)vvv)
+ *
+ * HERE BE DRAGONS
+ *
+ */
+
+package com.github.shadowsocks
+
+import android.app.Service
+import android.content._
+import android.net.VpnService
+import android.os._
+import android.preference.PreferenceManager
+import com.github.shadowsocks.aidl.IShadowsocksService
+import com.github.shadowsocks.utils._
+
+class ShadowsocksRunnerService extends Service {
+
+ private lazy val settings = PreferenceManager.getDefaultSharedPreferences(this)
+ private lazy val status = getSharedPreferences(Key.status, Context.MODE_PRIVATE)
+
+ val handler = new Handler()
+ val connection = new ServiceConnection {
+ override def onServiceConnected(name: ComponentName, service: IBinder) {
+ bgService = IShadowsocksService.Stub.asInterface(service)
+ handler.postDelayed(() => if (bgService != null) startBackgroundService(), 1000)
+ }
+ override def onServiceDisconnected(name: ComponentName) {
+ bgService = null
+ }
+ }
+
+ // Variables
+ var vpnEnabled = -1
+ var bgService: IShadowsocksService = null
+
+ def isVpnEnabled: Boolean = {
+ if (vpnEnabled < 0) {
+ vpnEnabled = if ((!Utils.isLollipopOrAbove || status.getBoolean(Key.isNAT, false)) && Console.isRoot) {
+ 0
+ } else {
+ 1
+ }
+ }
+ if (vpnEnabled == 1) true else false
+ }
+
+ override def onBind(intent: Intent): IBinder = {
+ null
+ }
+
+ def startBackgroundService() {
+ if (isVpnEnabled) {
+ val intent = VpnService.prepare(ShadowsocksRunnerService.this)
+ if (intent == null) {
+ if (bgService != null) {
+ bgService.start(ConfigUtils.load(settings))
+ }
+ }
+ } else {
+ bgService.start(ConfigUtils.load(settings))
+ }
+ stopSelf()
+ }
+
+ def attachService() {
+ if (bgService == null) {
+ val s = if (!isVpnEnabled) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService]
+ val intent = new Intent(this, s)
+ intent.setAction(Action.SERVICE)
+ bindService(intent, connection, Context.BIND_AUTO_CREATE)
+ startService(new Intent(this, s))
+ }
+ }
+
+ def deattachService() {
+ if (bgService != null) {
+ unbindService(connection)
+ bgService = null
+ }
+ }
+
+ override def onCreate() {
+ super.onCreate()
+ attachService()
+ }
+
+ override def onDestroy() {
+ super.onDestroy()
+ deattachService()
+ }
+}