Skip to content

Commit

Permalink
force VpnService as a foreground service
Browse files Browse the repository at this point in the history
  • Loading branch information
madeye committed Oct 5, 2015
1 parent bf3933b commit ff0ddd5
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 92 deletions.
87 changes: 2 additions & 85 deletions src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,25 +69,13 @@ class ShadowsocksNatService extends Service with BaseService {
val CMD_IPTABLES_DNAT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " +
"-j DNAT --to-destination 127.0.0.1:8123"

private val mStartForegroundSignature = Array[Class[_]](classOf[Int], classOf[Notification])
private val mStopForegroundSignature = Array[Class[_]](classOf[Boolean])
private val mSetForegroundSignature = Array[Class[_]](classOf[Boolean])
private val mSetForegroundArgs = new Array[AnyRef](1)
private val mStartForegroundArgs = new Array[AnyRef](2)
private val mStopForegroundArgs = new Array[AnyRef](1)

var lockReceiver: BroadcastReceiver = null
var closeReceiver: BroadcastReceiver = null
var connReceiver: BroadcastReceiver = null
var notificationManager: NotificationManager = null
var config: Config = null
var apps: Array[ProxiedApp] = null
val myUid = Process.myUid()

private var mSetForeground: Method = null
private var mStartForeground: Method = null
private var mStopForeground: Method = null

private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]

private val dnsAddressCache = new SparseArray[String]
Expand Down Expand Up @@ -321,17 +309,6 @@ class ShadowsocksNatService extends Service with BaseService {
true
}

def invokeMethod(method: Method, args: Array[AnyRef]) {
try {
method.invoke(this, mStartForegroundArgs: _*)
} catch {
case e: InvocationTargetException =>
Log.w(TAG, "Unable to invoke method", e)
case e: IllegalAccessException =>
Log.w(TAG, "Unable to invoke method", e)
}
}

def notifyForegroundAlert(title: String, info: String, visible: Boolean) {
val openIntent = new Intent(this, classOf[Shadowsocks])
openIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
Expand All @@ -355,7 +332,7 @@ class ShadowsocksNatService extends Service with BaseService {
else
builder.setPriority(NotificationCompat.PRIORITY_MIN)

startForegroundCompat(1, builder.build)
startForeground(1, builder.build)
}

def onBind(intent: Intent): IBinder = {
Expand All @@ -369,29 +346,7 @@ class ShadowsocksNatService extends Service with BaseService {

override def onCreate() {
super.onCreate()

ConfigUtils.refresh(this)

notificationManager = this
.getSystemService(Context.NOTIFICATION_SERVICE)
.asInstanceOf[NotificationManager]
try {
mStartForeground = getClass.getMethod("startForeground", mStartForegroundSignature: _*)
mStopForeground = getClass.getMethod("stopForeground", mStopForegroundSignature: _*)
} catch {
case e: NoSuchMethodException =>
mStartForeground = {
mStopForeground = null
mStopForeground
}
}
try {
mSetForeground = getClass.getMethod("setForeground", mSetForegroundSignature: _*)
} catch {
case e: NoSuchMethodException =>
throw new IllegalStateException(
"OS doesn't have Service.startForeground OR Service.setForeground!")
}
}

def killProcesses() {
Expand Down Expand Up @@ -462,44 +417,6 @@ class ShadowsocksNatService extends Service with BaseService {
Console.runRootCommand(http_sb.toArray)
}

/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
*/
def startForegroundCompat(id: Int, notification: Notification) {
if (mStartForeground != null) {
mStartForegroundArgs(0) = int2Integer(id)
mStartForegroundArgs(1) = notification
invokeMethod(mStartForeground, mStartForegroundArgs)
return
}
mSetForegroundArgs(0) = boolean2Boolean(x = true)
invokeMethod(mSetForeground, mSetForegroundArgs)
notificationManager.notify(id, notification)
}

/**
* This is a wrapper around the new stopForeground method, using the older
* APIs if it is not available.
*/
def stopForegroundCompat(id: Int) {
if (mStopForeground != null) {
mStopForegroundArgs(0) = boolean2Boolean(x = true)
try {
mStopForeground.invoke(this, mStopForegroundArgs: _*)
} catch {
case e: InvocationTargetException =>
Log.w(TAG, "Unable to invoke stopForeground", e)
case e: IllegalAccessException =>
Log.w(TAG, "Unable to invoke stopForeground", e)
}
return
}
notificationManager.cancel(id)
mSetForegroundArgs(0) = boolean2Boolean(x = false)
invokeMethod(mSetForeground, mSetForegroundArgs)
}

override def startRunner(c: Config) {

config = c
Expand Down Expand Up @@ -627,7 +544,7 @@ class ShadowsocksNatService extends Service with BaseService {
stopSelf()
}

stopForegroundCompat(1)
stopForeground(true)

// change the state
changeState(State.STOPPED)
Expand Down
37 changes: 30 additions & 7 deletions src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import android.content._
import android.content.pm.{PackageInfo, PackageManager}
import android.net.VpnService
import android.os._
import android.support.v4.app.NotificationCompat
import android.util.Log
import android.widget.Toast
import com.github.shadowsocks.aidl.Config
Expand All @@ -67,7 +68,6 @@ class ShadowsocksVpnService extends VpnService with BaseService {
val PRIVATE_VLAN = "26.26.26.%s"
val PRIVATE_VLAN6 = "fdfe:dcba:9876::%s"
var conn: ParcelFileDescriptor = null
var notificationManager: NotificationManager = null
var receiver: BroadcastReceiver = null
var apps: Array[ProxiedApp] = null
var config: Config = null
Expand Down Expand Up @@ -104,15 +104,34 @@ class ShadowsocksVpnService extends VpnService with BaseService {
null
}

override def onCreate() {

super.onCreate()
def notifyForegroundAlert(title: String, info: String, visible: Boolean) {
val openIntent = new Intent(this, classOf[Shadowsocks])
val contentIntent = PendingIntent.getActivity(this, 0, openIntent, 0)
val closeIntent = new Intent(Action.CLOSE)
val actionIntent = PendingIntent.getBroadcast(this, 0, closeIntent, 0)
val builder = new NotificationCompat.Builder(this)

ConfigUtils.refresh(this)
builder
.setWhen(0)
.setTicker(title)
.setContentTitle(getString(R.string.app_name))
.setContentText(info)
.setContentIntent(contentIntent)
.setSmallIcon(R.drawable.ic_stat_shadowsocks)
.addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.stop),
actionIntent)

if (visible)
builder.setPriority(NotificationCompat.PRIORITY_DEFAULT)
else
builder.setPriority(NotificationCompat.PRIORITY_MIN)

notificationManager = getSystemService(Context.NOTIFICATION_SERVICE)
.asInstanceOf[NotificationManager]
startForeground(1, builder.build)
}

override def onCreate() {
super.onCreate()
ConfigUtils.refresh(this)
}

override def onRevoke() {
Expand All @@ -126,6 +145,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
vpnThread = null
}

stopForeground(true)

// channge the state
changeState(State.STOPPING)

Expand Down Expand Up @@ -248,6 +269,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
}

if (resolved && handleConnection) {
notifyForegroundAlert(getString(R.string.forward_success),
getString(R.string.service_running).formatLocal(Locale.ENGLISH, config.profileName), false)
changeState(State.CONNECTED)
} else {
changeState(State.STOPPED, getString(R.string.service_failed))
Expand Down

0 comments on commit ff0ddd5

Please sign in to comment.