From 424e1bd1bcfdc4d2d281249a58468f52a42b3067 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 18 May 2017 14:56:26 +0800 Subject: [PATCH] Update to Android O preview 2 * Update support library to beta1; * Adapt to new foreground service policy; * Add a separate notification channel for NAT service; * Adapt to `findViewById` method signature changes. Possible issues: NAT notification will possibly not hide in lockscreen. --- .travis.yml | 2 +- build.sbt | 6 ++++-- mobile/src/main/res/values/strings.xml | 3 ++- .../main/scala/com/github/shadowsocks/BaseService.scala | 9 ++++++++- .../main/scala/com/github/shadowsocks/MainActivity.scala | 2 +- .../com/github/shadowsocks/ShadowsocksApplication.scala | 7 +++++-- .../com/github/shadowsocks/ShadowsocksNatService.scala | 6 ++---- .../com/github/shadowsocks/ShadowsocksNotification.scala | 9 ++++----- .../com/github/shadowsocks/ShadowsocksVpnService.scala | 7 ++----- 9 files changed, 29 insertions(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index a9a15ed4..469233a1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ jdk: env: global: - - NDK_VERSION=r14 + - NDK_VERSION=r15-beta2 - NDK_CCACHE=ccache - GOROOT_BOOTSTRAP=$GOROOT - ANDROID_NDK_HOME=$HOME/.android/android-ndk-${NDK_VERSION} diff --git a/build.sbt b/build.sbt index ff88b127..5f6450c5 100644 --- a/build.sbt +++ b/build.sbt @@ -18,10 +18,12 @@ lazy val commonSettings = Seq( shrinkResources := true, typedResources := false, - resConfigs := Seq("ja", "ko", "ru", "zh-rCN", "zh-rTW") + resConfigs := Seq("ja", "ko", "ru", "zh-rCN", "zh-rTW"), + + resolvers += "google" at "https://maven.google.com" ) -val supportLibsVersion = "26.0.0-alpha1" +val supportLibsVersion = "26.0.0-beta1" lazy val root = Project(id = "shadowsocks-android", base = file(".")) .settings(commonSettings) .aggregate(plugin, mobile) diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index 594b7fc7..0d937680 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -56,7 +56,8 @@ Forward all DNS requests to remote - Service + VPN Service + NAT Service Shadowsocks started. Invalid server name Failed to connect the remote server diff --git a/mobile/src/main/scala/com/github/shadowsocks/BaseService.scala b/mobile/src/main/scala/com/github/shadowsocks/BaseService.scala index b9a8ac02..30e4b977 100644 --- a/mobile/src/main/scala/com/github/shadowsocks/BaseService.scala +++ b/mobile/src/main/scala/com/github/shadowsocks/BaseService.scala @@ -30,6 +30,7 @@ import java.net.Inet6Address import android.app.Service import android.content.{BroadcastReceiver, Context, Intent, IntentFilter} import android.os.{Handler, IBinder, RemoteCallbackList} +import android.support.v4.os.BuildCompat import android.text.TextUtils import android.util.Log import android.widget.Toast @@ -65,6 +66,7 @@ trait BaseService extends Service { lazy val handler = new Handler(getMainLooper) lazy val restartHanlder = new Handler(getMainLooper) + private var notification: ShadowsocksNotification = _ private val closeReceiver: BroadcastReceiver = (context: Context, _: Intent) => { Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show() stopRunner(stopService = true) @@ -160,10 +162,12 @@ trait BaseService extends Service { pluginPath = PluginManager.init(plugin) } + def createNotification(): ShadowsocksNotification def startRunner(profile: Profile) { this.profile = profile - startService(new Intent(this, getClass)) + if (BuildCompat.isAtLeastO) startForegroundService(new Intent(this, getClass)) + else startService(new Intent(this, getClass)) TrafficMonitor.reset() trafficMonitorThread = new TrafficMonitorThread(getApplicationContext) trafficMonitorThread.start() @@ -177,6 +181,7 @@ trait BaseService extends Service { closeReceiverRegistered = true } + notification = createNotification() app.track(getClass.getSimpleName, "start") changeState(State.CONNECTING) @@ -198,6 +203,8 @@ trait BaseService extends Service { closeReceiverRegistered = false } + if (notification != null) notification.destroy() + // Make sure update total traffic when stopping the runner updateTrafficTotal(TrafficMonitor.txTotal, TrafficMonitor.rxTotal) diff --git a/mobile/src/main/scala/com/github/shadowsocks/MainActivity.scala b/mobile/src/main/scala/com/github/shadowsocks/MainActivity.scala index 3374eda8..b3aae6e0 100644 --- a/mobile/src/main/scala/com/github/shadowsocks/MainActivity.scala +++ b/mobile/src/main/scala/com/github/shadowsocks/MainActivity.scala @@ -256,7 +256,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe txRateText = findViewById(R.id.txRate).asInstanceOf[TextView] rxText = findViewById(R.id.rx).asInstanceOf[TextView] rxRateText = findViewById(R.id.rxRate).asInstanceOf[TextView] - findViewById(R.id.stat).setOnClickListener(_ => if (state == State.CONNECTED && app.isVpnEnabled) { + findViewById[View](R.id.stat).setOnClickListener(_ => if (state == State.CONNECTED && app.isVpnEnabled) { testCount += 1 statusText.setText(R.string.connection_test_testing) val id = testCount // it would change by other code diff --git a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala index 4dd063a6..72f8b006 100644 --- a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala +++ b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala @@ -44,6 +44,7 @@ import com.j256.ormlite.logger.LocalLog import eu.chainfire.libsuperuser.Shell import scala.collection.mutable.ArrayBuffer +import scala.collection.JavaConversions._ object ShadowsocksApplication { var app: ShadowsocksApplication = _ @@ -156,8 +157,10 @@ class ShadowsocksApplication extends Application { TcpFastOpen.enabled(settings.getBoolean(Key.tfo, TcpFastOpen.sendEnabled)) - if (BuildCompat.isAtLeastO) getSystemService(classOf[NotificationManager]).createNotificationChannel( - new NotificationChannel("service", getText(R.string.service), NotificationManager.IMPORTANCE_MIN)) + if (BuildCompat.isAtLeastO) getSystemService(classOf[NotificationManager]).createNotificationChannels(List( + new NotificationChannel("service-vpn", getText(R.string.service_vpn), NotificationManager.IMPORTANCE_MIN), + new NotificationChannel("service-nat", getText(R.string.service_nat), NotificationManager.IMPORTANCE_LOW) + )) } def crashRecovery() { diff --git a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala index d5855834..a71ea95d 100644 --- a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala +++ b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala @@ -43,7 +43,6 @@ class ShadowsocksNatService extends BaseService { val CMD_IPTABLES_DNAT_ADD_SOCKS = "iptables -t nat -A OUTPUT -p tcp -j DNAT --to-destination 127.0.0.1:8123" - private var notification: ShadowsocksNotification = _ val myUid: Int = android.os.Process.myUid() var sslocalProcess: GuardedProcess = _ @@ -204,12 +203,11 @@ class ShadowsocksNatService extends BaseService { AclSyncJob.schedule(profile.route) changeState(State.CONNECTED) - notification = new ShadowsocksNotification(this, profile.name, true) } - override def stopRunner(stopService: Boolean, msg: String = null) { + override def createNotification() = new ShadowsocksNotification(this, profile.name, "service-nat", true) - if (notification != null) notification.destroy() + override def stopRunner(stopService: Boolean, msg: String = null) { // channge the state changeState(State.STOPPING) diff --git a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala index 9fc38fc9..7ca79437 100644 --- a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala +++ b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala @@ -35,7 +35,8 @@ import com.github.shadowsocks.utils.{Action, State, TrafficMonitor, Utils} /** * @author Mygod */ -class ShadowsocksNotification(private val service: BaseService, profileName: String, visible: Boolean = false) { +class ShadowsocksNotification(private val service: BaseService, profileName: String, + channel: String, visible: Boolean = false) { private val keyGuard = service.getSystemService(Context.KEYGUARD_SERVICE).asInstanceOf[KeyguardManager] private lazy val nm = service.getSystemService(Context.NOTIFICATION_SERVICE).asInstanceOf[NotificationManager] private lazy val callback = new Stub { @@ -61,7 +62,7 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str .setContentIntent(PendingIntent.getActivity(service, 0, new Intent(service, classOf[MainActivity]) .setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT), 0)) .setSmallIcon(R.drawable.ic_stat_shadowsocks) - .setChannel("service") + .setChannel(channel) if (Build.VERSION.SDK_INT < 24) builder.addAction(R.drawable.ic_navigation_close, service.getString(R.string.stop), PendingIntent.getBroadcast(service, 0, new Intent(Action.CLOSE), 0)) private lazy val style = new BigTextStyle(builder) @@ -99,9 +100,7 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str show() } else if (forceShow) show() - def show(): Unit = - if (BuildCompat.isAtLeastO) nm.startServiceInForeground(new Intent(service, service.getClass), 1, builder.build()) - else service.startForeground(1, builder.build) + def show(): Unit = service.startForeground(1, builder.build) def destroy() { if (lockReceiver != null) { diff --git a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala index 81ac83ee..5e4d1e34 100644 --- a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala +++ b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala @@ -42,7 +42,6 @@ class ShadowsocksVpnService extends VpnService with BaseService { val PRIVATE_VLAN6 = "fdfe:dcba:9876::%s" var conn: ParcelFileDescriptor = _ var vpnThread: ShadowsocksVpnThread = _ - private var notification: ShadowsocksNotification = _ var sslocalProcess: GuardedProcess = _ var overtureProcess: GuardedProcess = _ @@ -69,8 +68,6 @@ class ShadowsocksVpnService extends VpnService with BaseService { vpnThread = null } - if (notification != null) notification.destroy() - // channge the state changeState(State.STOPPING) @@ -117,6 +114,8 @@ class ShadowsocksVpnService extends VpnService with BaseService { super.startRunner(profile) } + override def createNotification() = new ShadowsocksNotification(this, profile.name, "service-vpn") + override def connect() { super.connect() @@ -137,8 +136,6 @@ class ShadowsocksVpnService extends VpnService with BaseService { if (profile.route != Acl.ALL && profile.route != Acl.CUSTOM_RULES) AclSyncJob.schedule(profile.route) - - notification = new ShadowsocksNotification(this, profile.name) } /** Called when the activity is first created. */