From 15b55957979fa80d9838a662940f542073763a79 Mon Sep 17 00:00:00 2001 From: yuhan6665 <1588741+yuhan6665@users.noreply.github.com> Date: Fri, 4 Sep 2020 18:19:51 -0400 Subject: [PATCH 1/6] Refactor service Break VpnService so that it only control vpn and tun2socks, Other function is moved to a singleton service manager. This code makes it possible to add proxy only service next. --- .../com/v2ray/ang/service/ServiceControl.kt | 15 + .../v2ray/ang/service/V2RayServiceManager.kt | 363 ++++++++++++++++++ .../com/v2ray/ang/service/V2RayVpnService.kt | 342 +---------------- .../main/kotlin/com/v2ray/ang/util/Utils.kt | 4 +- 4 files changed, 396 insertions(+), 328 deletions(-) create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/ServiceControl.kt create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/ServiceControl.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/ServiceControl.kt new file mode 100644 index 00000000..6fd22c79 --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/ServiceControl.kt @@ -0,0 +1,15 @@ +package com.v2ray.ang.service + +import android.app.Service + +interface ServiceControl { + fun getService(): Service + + fun startService(parameters: String) + + fun stopService() + + fun vpnProtect(socket: Int): Boolean + + fun vpnSendFd() +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt new file mode 100644 index 00000000..66682b7a --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt @@ -0,0 +1,363 @@ +package com.v2ray.ang.service + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.graphics.Color +import android.os.Build +import android.support.annotation.RequiresApi +import android.support.v4.app.NotificationCompat +import android.util.Log +import com.v2ray.ang.AppConfig +import com.v2ray.ang.AppConfig.TAG_DIRECT +import com.v2ray.ang.R +import com.v2ray.ang.extension.defaultDPreference +import com.v2ray.ang.extension.toSpeedString +import com.v2ray.ang.ui.MainActivity +import com.v2ray.ang.ui.SettingsActivity +import com.v2ray.ang.util.MessageUtil +import com.v2ray.ang.util.Utils +import go.Seq +import libv2ray.Libv2ray +import libv2ray.V2RayVPNServiceSupportsSet +import rx.Observable +import rx.Subscription +import java.lang.ref.SoftReference + +object V2RayServiceManager { + private const val NOTIFICATION_ID = 1 + private const val NOTIFICATION_PENDING_INTENT_CONTENT = 0 + private const val NOTIFICATION_PENDING_INTENT_STOP_V2RAY = 1 + private const val NOTIFICATION_ICON_THRESHOLD = 3000 + + private val v2rayPoint = Libv2ray.newV2RayPoint(V2RayCallback()) + private val mMsgReceive = ReceiveMessageHandler() + + var serviceControl: SoftReference? = null + set(value) { + field = value + val context = value?.get()?.getService()?.applicationContext + context?.let { + v2rayPoint.packageName = Utils.packagePath(context) + v2rayPoint.packageCodePath = context.applicationInfo.nativeLibraryDir + "/" + Seq.setContext(context) + } + } + + private var lastQueryTime = 0L + private var mBuilder: NotificationCompat.Builder? = null + private var mSubscription: Subscription? = null + private var mNotificationManager: NotificationManager? = null + + fun startV2Ray(context: Context) { + val intent = Intent(context.applicationContext, V2RayVpnService::class.java) + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) { + context.startForegroundService(intent) + } else { + context.startService(intent) + } + } + + private class V2RayCallback : V2RayVPNServiceSupportsSet { + override fun shutdown(): Long { + val serviceControl = serviceControl?.get() ?: return -1 + // called by go + // shutdown the whole vpn service + try { + serviceControl.stopService() + return 0 + } catch (e: Exception) { + Log.d(serviceControl.getService().packageName, e.toString()) + return -1 + } + } + + override fun prepare(): Long { + return 0 + } + + override fun protect(l: Long): Long { + val serviceControl = serviceControl?.get() ?: return 1 + if (serviceControl.vpnProtect(l.toInt())) + return 0 + else + return 1 + } + + override fun onEmitStatus(l: Long, s: String?): Long { + //Logger.d(s) + return 0 + } + + override fun setup(s: String): Long { + val serviceControl = serviceControl?.get() ?: return -1 + //Logger.d(s) + try { + serviceControl.startService(s) + lastQueryTime = System.currentTimeMillis() + startSpeedNotification() + return 0 + } catch (e: Exception) { + Log.d(serviceControl.getService().packageName, e.toString()) + return -1 + } + } + + override fun sendFd(): Long { + val serviceControl = serviceControl?.get() ?: return -1 + try { + serviceControl.vpnSendFd() + } catch (e: Exception) { + Log.d(serviceControl.getService().packageName, e.toString()) + return -1 + } + return 0 + } + } + + fun startV2rayPoint() { + val service = serviceControl?.get()?.getService() ?: return + if (!v2rayPoint.isRunning) { + + try { + val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_SERVICE) + mFilter.addAction(Intent.ACTION_SCREEN_ON) + mFilter.addAction(Intent.ACTION_SCREEN_OFF) + mFilter.addAction(Intent.ACTION_USER_PRESENT) + service.registerReceiver(mMsgReceive, mFilter) + } catch (e: Exception) { + } + + v2rayPoint.configureFileContent = service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "") + v2rayPoint.enableLocalDNS = service.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_LOCAL_DNS_ENABLED, false) + v2rayPoint.forwardIpv6 = service.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_FORWARD_IPV6, false) + v2rayPoint.domainName = service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, "") + + try { + v2rayPoint.runLoop() + } catch (e: Exception) { + Log.d(service.packageName, e.toString()) + } + + if (v2rayPoint.isRunning) { + MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_SUCCESS, "") + showNotification() + } else { + MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_FAILURE, "") + cancelNotification() + } + } + // showNotification() + } + + fun stopV2rayPoint() { + val service = serviceControl?.get()?.getService() ?: return + + if (v2rayPoint.isRunning) { + try { + v2rayPoint.stopLoop() + } catch (e: Exception) { + Log.d(service.packageName, e.toString()) + } + } + + MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_STOP_SUCCESS, "") + cancelNotification() + + try { + service.unregisterReceiver(mMsgReceive) + } catch (e: Exception) { + } + } + + private class ReceiveMessageHandler : BroadcastReceiver() { + override fun onReceive(ctx: Context?, intent: Intent?) { + val serviceControl = serviceControl?.get() ?: return + when (intent?.getIntExtra("key", 0)) { + AppConfig.MSG_REGISTER_CLIENT -> { + //Logger.e("ReceiveMessageHandler", intent?.getIntExtra("key", 0).toString()) + if (v2rayPoint.isRunning) { + MessageUtil.sendMsg2UI(serviceControl.getService(), AppConfig.MSG_STATE_RUNNING, "") + } else { + MessageUtil.sendMsg2UI(serviceControl.getService(), AppConfig.MSG_STATE_NOT_RUNNING, "") + } + } + AppConfig.MSG_UNREGISTER_CLIENT -> { +// vpnService?.mMsgSend = null + } + AppConfig.MSG_STATE_START -> { + //nothing to do + } + AppConfig.MSG_STATE_STOP -> { + serviceControl.stopService() + } + AppConfig.MSG_STATE_RESTART -> { + startV2rayPoint() + } + } + + when (intent?.action) { + Intent.ACTION_SCREEN_OFF -> { + Log.d(AppConfig.ANG_PACKAGE, "SCREEN_OFF, stop querying stats") + stopSpeedNotification() + } + Intent.ACTION_SCREEN_ON -> { + Log.d(AppConfig.ANG_PACKAGE, "SCREEN_ON, start querying stats") + startSpeedNotification() + } + } + } + } + + private fun showNotification() { + val service = serviceControl?.get()?.getService() ?: return + val startMainIntent = Intent(service, MainActivity::class.java) + val contentPendingIntent = PendingIntent.getActivity(service, + NOTIFICATION_PENDING_INTENT_CONTENT, startMainIntent, + PendingIntent.FLAG_UPDATE_CURRENT) + + val stopV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE) + stopV2RayIntent.`package` = AppConfig.ANG_PACKAGE + stopV2RayIntent.putExtra("key", AppConfig.MSG_STATE_STOP) + + val stopV2RayPendingIntent = PendingIntent.getBroadcast(service, + NOTIFICATION_PENDING_INTENT_STOP_V2RAY, stopV2RayIntent, + PendingIntent.FLAG_UPDATE_CURRENT) + + val channelId = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel() + } else { + // If earlier version channel ID is not used + // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context) + "" + } + + mBuilder = NotificationCompat.Builder(service, channelId) + .setSmallIcon(R.drawable.ic_v) + .setContentTitle(service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "")) + .setPriority(NotificationCompat.PRIORITY_MIN) + .setOngoing(true) + .setShowWhen(false) + .setOnlyAlertOnce(true) + .setContentIntent(contentPendingIntent) + .addAction(R.drawable.ic_close_grey_800_24dp, + service.getString(R.string.notification_action_stop_v2ray), + stopV2RayPendingIntent) + //.build() + + //mBuilder?.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE) //取消震动,铃声其他都不好使 + + service.startForeground(NOTIFICATION_ID, mBuilder?.build()) + } + + @RequiresApi(Build.VERSION_CODES.O) + private fun createNotificationChannel(): String { + val channelId = "RAY_NG_M_CH_ID" + val channelName = "V2rayNG Background Service" + val chan = NotificationChannel(channelId, + channelName, NotificationManager.IMPORTANCE_HIGH) + chan.lightColor = Color.DKGRAY + chan.importance = NotificationManager.IMPORTANCE_NONE + chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE + getNotificationManager()?.createNotificationChannel(chan) + return channelId + } + + fun cancelNotification() { + val service = serviceControl?.get()?.getService() ?: return + service.stopForeground(true) + mBuilder = null + mSubscription?.unsubscribe() + mSubscription = null + } + + private fun updateNotification(contentText: String, proxyTraffic: Long, directTraffic: Long) { + if (mBuilder != null) { + if (proxyTraffic < NOTIFICATION_ICON_THRESHOLD && directTraffic < NOTIFICATION_ICON_THRESHOLD) { + mBuilder?.setSmallIcon(R.drawable.ic_v) + } else if (proxyTraffic > directTraffic) { + mBuilder?.setSmallIcon(R.drawable.ic_stat_proxy) + } else { + mBuilder?.setSmallIcon(R.drawable.ic_stat_direct) + } + mBuilder?.setStyle(NotificationCompat.BigTextStyle().bigText(contentText)) + mBuilder?.setContentText(contentText) // Emui4.1 need content text even if style is set as BigTextStyle + getNotificationManager()?.notify(NOTIFICATION_ID, mBuilder?.build()) + } + } + + private fun getNotificationManager(): NotificationManager? { + if (mNotificationManager == null) { + val service = serviceControl?.get()?.getService() ?: return null + mNotificationManager = service.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + } + return mNotificationManager + } + + fun startSpeedNotification() { + val service = serviceControl?.get()?.getService() ?: return + if (mSubscription == null && + v2rayPoint.isRunning && + service.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_SPEED_ENABLED, false)) { + var last_zero_speed = false + val outboundTags = service.defaultDPreference.getPrefStringOrderedSet(AppConfig.PREF_CURR_CONFIG_OUTBOUND_TAGS, LinkedHashSet()) + outboundTags.remove(TAG_DIRECT) + + mSubscription = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS) + .subscribe { + val queryTime = System.currentTimeMillis() + val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0 + var proxyTotal = 0L + val text = StringBuilder() + outboundTags.forEach { + val up = v2rayPoint.queryStats(it, "uplink") + val down = v2rayPoint.queryStats(it, "downlink") + if (up + down > 0) { + appendSpeedString(text, it, up / sinceLastQueryInSeconds, down / sinceLastQueryInSeconds) + proxyTotal += up + down + } + } + val directUplink = v2rayPoint.queryStats(TAG_DIRECT, "uplink") + val directDownlink = v2rayPoint.queryStats(TAG_DIRECT, "downlink") + val zero_speed = (proxyTotal == 0L && directUplink == 0L && directDownlink == 0L) + if (!zero_speed || !last_zero_speed) { + if (proxyTotal == 0L) { + appendSpeedString(text, outboundTags.firstOrNull(), 0.0, 0.0) + } + appendSpeedString(text, TAG_DIRECT, directUplink / sinceLastQueryInSeconds, + directDownlink / sinceLastQueryInSeconds) + updateNotification(text.toString(), proxyTotal, directDownlink + directUplink) + } + last_zero_speed = zero_speed + lastQueryTime = queryTime + } + } + } + + private fun appendSpeedString(text: StringBuilder, name: String?, up: Double, down: Double) { + var n = name ?: "no tag" + n = n.substring(0, Math.min(n.length, 6)) + text.append(n) + for (i in n.length..6 step 2) { + text.append("\t") + } + text.append("• ${up.toLong().toSpeedString()}↑ ${down.toLong().toSpeedString()}↓\n") + } + + fun stopSpeedNotification() { + val service = serviceControl?.get()?.getService() ?: return + if (mSubscription != null) { + mSubscription?.unsubscribe() //stop queryStats + mSubscription = null + + val cf_name = service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "") + updateNotification(cf_name, 0, 0) + } + } +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt index 2eea5d5c..0567f634 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt @@ -1,67 +1,27 @@ package com.v2ray.ang.service -import android.app.Notification -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.PendingIntent -import android.content.BroadcastReceiver +import android.app.* import android.content.Context import android.content.Intent -import android.content.IntentFilter import android.content.pm.PackageManager -import android.graphics.Color import android.net.* import android.os.Build import android.os.ParcelFileDescriptor import android.os.StrictMode import android.support.annotation.RequiresApi -import android.support.v4.app.NotificationCompat import android.util.Log import com.v2ray.ang.AppConfig -import com.v2ray.ang.AppConfig.TAG_DIRECT import com.v2ray.ang.R import com.v2ray.ang.extension.defaultDPreference -import com.v2ray.ang.extension.toSpeedString -import com.v2ray.ang.ui.MainActivity import com.v2ray.ang.ui.PerAppProxyActivity import com.v2ray.ang.ui.SettingsActivity -import com.v2ray.ang.util.MessageUtil import com.v2ray.ang.util.Utils -import go.Seq -import libv2ray.Libv2ray -import libv2ray.V2RayVPNServiceSupportsSet import org.jetbrains.anko.doAsync -import rx.Observable -import rx.Subscription import java.io.File import java.lang.ref.SoftReference -class V2RayVpnService : VpnService() { - companion object { - const val NOTIFICATION_ID = 1 - const val NOTIFICATION_PENDING_INTENT_CONTENT = 0 - const val NOTIFICATION_PENDING_INTENT_STOP_V2RAY = 1 - const val NOTIFICATION_ICON_THRESHOLD = 3000 - - fun startV2Ray(context: Context) { - val intent = Intent(context.applicationContext, V2RayVpnService::class.java) - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) { - context.startForegroundService(intent) - } else { - context.startService(intent) - } - } - } - - private val v2rayPoint = Libv2ray.newV2RayPoint(V2RayCallback()) - private var lastQueryTime = 0L - private lateinit var configContent: String +class V2RayVpnService : VpnService(), ServiceControl { private lateinit var mInterface: ParcelFileDescriptor - val fd: Int get() = mInterface.fd - private var mBuilder: NotificationCompat.Builder? = null - private var mSubscription: Subscription? = null - private var mNotificationManager: NotificationManager? = null - /** * Unfortunately registerDefaultNetworkCallback is going to return our VPN interface: https://android.googlesource.com/platform/frameworks/base/+/dda156ab0c5d66ad82bdcf76cda07cbc0a9c8a2e @@ -103,9 +63,7 @@ class V2RayVpnService : VpnService() { val policy = StrictMode.ThreadPolicy.Builder().permitAll().build() StrictMode.setThreadPolicy(policy) - v2rayPoint.packageName = Utils.packagePath(applicationContext) - v2rayPoint.packageCodePath = applicationContext.applicationInfo.nativeLibraryDir + "/" - Seq.setContext(applicationContext) + V2RayServiceManager.serviceControl = SoftReference(this) } override fun onRevoke() { @@ -119,7 +77,7 @@ class V2RayVpnService : VpnService() { override fun onDestroy() { super.onDestroy() - cancelNotification() + V2RayServiceManager.cancelNotification() } fun setup(parameters: String) { @@ -200,12 +158,6 @@ class V2RayVpnService : VpnService() { // Create a new interface using the builder and save the parameters. mInterface = builder.establish() sendFd() - lastQueryTime = System.currentTimeMillis() - startSpeedNotification() - } - - fun shutdown() { - stopV2Ray(true) } fun sendFd() { @@ -232,46 +184,11 @@ class V2RayVpnService : VpnService() { } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - startV2ray() + V2RayServiceManager.startV2rayPoint() return START_STICKY //return super.onStartCommand(intent, flags, startId) } - private fun startV2ray() { - if (!v2rayPoint.isRunning) { - - try { - val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_SERVICE) - mFilter.addAction(Intent.ACTION_SCREEN_ON) - mFilter.addAction(Intent.ACTION_SCREEN_OFF) - mFilter.addAction(Intent.ACTION_USER_PRESENT) - registerReceiver(mMsgReceive, mFilter) - } catch (e: Exception) { - } - - configContent = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "") - v2rayPoint.configureFileContent = configContent - v2rayPoint.enableLocalDNS = defaultDPreference.getPrefBoolean(SettingsActivity.PREF_LOCAL_DNS_ENABLED, false) - v2rayPoint.forwardIpv6 = defaultDPreference.getPrefBoolean(SettingsActivity.PREF_FORWARD_IPV6, false) - v2rayPoint.domainName = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_DOMAIN, "") - - try { - v2rayPoint.runLoop() - } catch (e: Exception) { - Log.d(packageName, e.toString()) - } - - if (v2rayPoint.isRunning) { - MessageUtil.sendMsg2UI(this, AppConfig.MSG_STATE_START_SUCCESS, "") - showNotification() - } else { - MessageUtil.sendMsg2UI(this, AppConfig.MSG_STATE_START_FAILURE, "") - cancelNotification() - } - } - // showNotification() - } - private fun stopV2Ray(isForced: Boolean = true) { // val configName = defaultDPreference.getPrefString(PREF_CURR_CONFIG_GUID, "") // val emptyInfo = VpnNetworkInfo() @@ -283,23 +200,10 @@ class V2RayVpnService : VpnService() { listeningForDefaultNetwork = false } } - if (v2rayPoint.isRunning) { - try { - v2rayPoint.stopLoop() - } catch (e: Exception) { - Log.d(packageName, e.toString()) - } - } - MessageUtil.sendMsg2UI(this, AppConfig.MSG_STATE_STOP_SUCCESS, "") - cancelNotification() + V2RayServiceManager.stopV2rayPoint() if (isForced) { - try { - unregisterReceiver(mMsgReceive) - } catch (e: Exception) { - } - //stopSelf has to be called ahead of mInterface.close(). otherwise v2ray core cannot be stooped //It's strage but true. //This can be verified by putting stopself() behind and call stopLoop and startLoop @@ -315,238 +219,24 @@ class V2RayVpnService : VpnService() { } } - private fun showNotification() { - val startMainIntent = Intent(applicationContext, MainActivity::class.java) - val contentPendingIntent = PendingIntent.getActivity(applicationContext, - NOTIFICATION_PENDING_INTENT_CONTENT, startMainIntent, - PendingIntent.FLAG_UPDATE_CURRENT) - - val stopV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE) - stopV2RayIntent.`package` = AppConfig.ANG_PACKAGE - stopV2RayIntent.putExtra("key", AppConfig.MSG_STATE_STOP) - - val stopV2RayPendingIntent = PendingIntent.getBroadcast(applicationContext, - NOTIFICATION_PENDING_INTENT_STOP_V2RAY, stopV2RayIntent, - PendingIntent.FLAG_UPDATE_CURRENT) - - val channelId = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - createNotificationChannel() - } else { - // If earlier version channel ID is not used - // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context) - "" - } - - mBuilder = NotificationCompat.Builder(applicationContext, channelId) - .setSmallIcon(R.drawable.ic_v) - .setContentTitle(defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "")) - .setPriority(NotificationCompat.PRIORITY_MIN) - .setOngoing(true) - .setShowWhen(false) - .setOnlyAlertOnce(true) - .setContentIntent(contentPendingIntent) - .addAction(R.drawable.ic_close_grey_800_24dp, - getString(R.string.notification_action_stop_v2ray), - stopV2RayPendingIntent) - //.build() - - //mBuilder?.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE) //取消震动,铃声其他都不好使 - - startForeground(NOTIFICATION_ID, mBuilder?.build()) + override fun getService(): Service { + return this } - @RequiresApi(Build.VERSION_CODES.O) - private fun createNotificationChannel(): String { - val channelId = "RAY_NG_M_CH_ID" - val channelName = "V2rayNG Background Service" - val chan = NotificationChannel(channelId, - channelName, NotificationManager.IMPORTANCE_HIGH) - chan.lightColor = Color.DKGRAY - chan.importance = NotificationManager.IMPORTANCE_NONE - chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE - getNotificationManager().createNotificationChannel(chan) - return channelId + override fun startService(parameters: String) { + setup(parameters) } - private fun cancelNotification() { - stopForeground(true) - mBuilder = null - mSubscription?.unsubscribe() - mSubscription = null + override fun stopService() { + stopV2Ray(true) } - private fun updateNotification(contentText: String, proxyTraffic: Long, directTraffic: Long) { - if (mBuilder != null) { - if (proxyTraffic < NOTIFICATION_ICON_THRESHOLD && directTraffic < NOTIFICATION_ICON_THRESHOLD) { - mBuilder?.setSmallIcon(R.drawable.ic_v) - } else if (proxyTraffic > directTraffic) { - mBuilder?.setSmallIcon(R.drawable.ic_stat_proxy) - } else { - mBuilder?.setSmallIcon(R.drawable.ic_stat_direct) - } - mBuilder?.setStyle(NotificationCompat.BigTextStyle().bigText(contentText)) - mBuilder?.setContentText(contentText) // Emui4.1 need content text even if style is set as BigTextStyle - getNotificationManager().notify(NOTIFICATION_ID, mBuilder?.build()) - } + override fun vpnProtect(socket: Int): Boolean { + return protect(socket) } - private fun getNotificationManager(): NotificationManager { - if (mNotificationManager == null) { - mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - } - return mNotificationManager!! - } - - fun startSpeedNotification() { - if (mSubscription == null && - v2rayPoint.isRunning && - defaultDPreference.getPrefBoolean(SettingsActivity.PREF_SPEED_ENABLED, false)) { - var last_zero_speed = false - val outboundTags = defaultDPreference.getPrefStringOrderedSet(AppConfig.PREF_CURR_CONFIG_OUTBOUND_TAGS, LinkedHashSet()) - outboundTags.remove(TAG_DIRECT) - - mSubscription = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS) - .subscribe { - val queryTime = System.currentTimeMillis() - val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0 - var proxyTotal = 0L - val text = StringBuilder() - outboundTags.forEach { - val up = v2rayPoint.queryStats(it, "uplink") - val down = v2rayPoint.queryStats(it, "downlink") - if (up + down > 0) { - appendSpeedString(text, it, up / sinceLastQueryInSeconds, down / sinceLastQueryInSeconds) - proxyTotal += up + down - } - } - val directUplink = v2rayPoint.queryStats(TAG_DIRECT, "uplink") - val directDownlink = v2rayPoint.queryStats(TAG_DIRECT, "downlink") - val zero_speed = (proxyTotal == 0L && directUplink == 0L && directDownlink == 0L) - if (!zero_speed || !last_zero_speed) { - if (proxyTotal == 0L) { - appendSpeedString(text, outboundTags.firstOrNull(), 0.0, 0.0) - } - appendSpeedString(text, TAG_DIRECT, directUplink / sinceLastQueryInSeconds, - directDownlink / sinceLastQueryInSeconds) - updateNotification(text.toString(), proxyTotal, directDownlink + directUplink) - } - last_zero_speed = zero_speed - lastQueryTime = queryTime - } - } - } - - private fun appendSpeedString(text: StringBuilder, name: String?, up: Double, down: Double) { - var n = name ?: "no tag" - n = n.substring(0, Math.min(n.length, 6)) - text.append(n) - for (i in n.length..6 step 2) { - text.append("\t") - } - text.append("• ${up.toLong().toSpeedString()}↑ ${down.toLong().toSpeedString()}↓\n") - } - - fun stopSpeedNotification() { - if (mSubscription != null) { - mSubscription?.unsubscribe() //stop queryStats - mSubscription = null - - val cf_name = defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "") - updateNotification(cf_name, 0, 0) - } - } - - private inner class V2RayCallback : V2RayVPNServiceSupportsSet { - override fun shutdown(): Long { - // called by go - // shutdown the whole vpn service - try { - this@V2RayVpnService.shutdown() - return 0 - } catch (e: Exception) { - Log.d(packageName, e.toString()) - return -1 - } - } - - override fun prepare(): Long { - return 0 - } - - override fun protect(l: Long) = (if (this@V2RayVpnService.protect(l.toInt())) 0 else 1).toLong() - - override fun onEmitStatus(l: Long, s: String?): Long { - //Logger.d(s) - return 0 - } - - override fun setup(s: String): Long { - //Logger.d(s) - try { - this@V2RayVpnService.setup(s) - return 0 - } catch (e: Exception) { - Log.d(packageName, e.toString()) - return -1 - } - } - - override fun sendFd(): Long { - try { - this@V2RayVpnService.sendFd() - } catch (e: Exception) { - Log.d(packageName, e.toString()) - return -1 - } - return 0 - } - } - - private var mMsgReceive = ReceiveMessageHandler(this@V2RayVpnService) - - private class ReceiveMessageHandler(vpnService: V2RayVpnService) : BroadcastReceiver() { - internal var mReference: SoftReference = SoftReference(vpnService) - - override fun onReceive(ctx: Context?, intent: Intent?) { - val vpnService = mReference.get() - when (intent?.getIntExtra("key", 0)) { - AppConfig.MSG_REGISTER_CLIENT -> { - //Logger.e("ReceiveMessageHandler", intent?.getIntExtra("key", 0).toString()) - - val isRunning = vpnService?.v2rayPoint!!.isRunning - && VpnService.prepare(vpnService) == null - if (isRunning) { - MessageUtil.sendMsg2UI(vpnService, AppConfig.MSG_STATE_RUNNING, "") - } else { - MessageUtil.sendMsg2UI(vpnService, AppConfig.MSG_STATE_NOT_RUNNING, "") - } - } - AppConfig.MSG_UNREGISTER_CLIENT -> { -// vpnService?.mMsgSend = null - } - AppConfig.MSG_STATE_START -> { - //nothing to do - } - AppConfig.MSG_STATE_STOP -> { - vpnService?.stopV2Ray() - } - AppConfig.MSG_STATE_RESTART -> { - vpnService?.startV2ray() - } - } - - when (intent?.action) { - Intent.ACTION_SCREEN_OFF -> { - Log.d(AppConfig.ANG_PACKAGE, "SCREEN_OFF, stop querying stats") - vpnService?.stopSpeedNotification() - } - Intent.ACTION_SCREEN_ON -> { - Log.d(AppConfig.ANG_PACKAGE, "SCREEN_ON, start querying stats") - vpnService?.startSpeedNotification() - } - } - } + override fun vpnSendFd() { + sendFd() } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt index 1320277d..11bb67ce 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt @@ -26,7 +26,7 @@ import com.v2ray.ang.R import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.extension.responseLength import com.v2ray.ang.extension.v2RayApplication -import com.v2ray.ang.service.V2RayVpnService +import com.v2ray.ang.service.V2RayServiceManager import com.v2ray.ang.ui.SettingsActivity import kotlinx.coroutines.isActive import me.dozen.dpreference.DPreference @@ -321,7 +321,7 @@ object Utils { return false } } - V2RayVpnService.startV2Ray(context) + V2RayServiceManager.startV2Ray(context) return true } else { return false From adfcf0a5d92fde39ddb357c27e802ed705b14e80 Mon Sep 17 00:00:00 2001 From: yuhan6665 <1588741+yuhan6665@users.noreply.github.com> Date: Fri, 4 Sep 2020 18:19:51 -0400 Subject: [PATCH 2/6] Fix some trivial IDE warnings --- .../v2ray/ang/service/V2RayServiceManager.kt | 39 +++++++++---------- .../com/v2ray/ang/service/V2RayVpnService.kt | 25 ++++++------ 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt index 66682b7a..07907e82 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt @@ -28,6 +28,7 @@ import libv2ray.V2RayVPNServiceSupportsSet import rx.Observable import rx.Subscription import java.lang.ref.SoftReference +import kotlin.math.min object V2RayServiceManager { private const val NOTIFICATION_ID = 1 @@ -68,12 +69,12 @@ object V2RayServiceManager { val serviceControl = serviceControl?.get() ?: return -1 // called by go // shutdown the whole vpn service - try { + return try { serviceControl.stopService() - return 0 + 0 } catch (e: Exception) { Log.d(serviceControl.getService().packageName, e.toString()) - return -1 + -1 } } @@ -83,10 +84,7 @@ object V2RayServiceManager { override fun protect(l: Long): Long { val serviceControl = serviceControl?.get() ?: return 1 - if (serviceControl.vpnProtect(l.toInt())) - return 0 - else - return 1 + return if (serviceControl.vpnProtect(l.toInt())) 0 else 1 } override fun onEmitStatus(l: Long, s: String?): Long { @@ -97,14 +95,14 @@ object V2RayServiceManager { override fun setup(s: String): Long { val serviceControl = serviceControl?.get() ?: return -1 //Logger.d(s) - try { + return try { serviceControl.startService(s) lastQueryTime = System.currentTimeMillis() startSpeedNotification() - return 0 + 0 } catch (e: Exception) { Log.d(serviceControl.getService().packageName, e.toString()) - return -1 + -1 } } @@ -131,6 +129,7 @@ object V2RayServiceManager { mFilter.addAction(Intent.ACTION_USER_PRESENT) service.registerReceiver(mMsgReceive, mFilter) } catch (e: Exception) { + Log.d(service.packageName, e.toString()) } v2rayPoint.configureFileContent = service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG, "") @@ -152,7 +151,6 @@ object V2RayServiceManager { cancelNotification() } } - // showNotification() } fun stopV2rayPoint() { @@ -172,6 +170,7 @@ object V2RayServiceManager { try { service.unregisterReceiver(mMsgReceive) } catch (e: Exception) { + Log.d(service.packageName, e.toString()) } } @@ -188,10 +187,10 @@ object V2RayServiceManager { } } AppConfig.MSG_UNREGISTER_CLIENT -> { -// vpnService?.mMsgSend = null + // nothing to do } AppConfig.MSG_STATE_START -> { - //nothing to do + // nothing to do } AppConfig.MSG_STATE_STOP -> { serviceControl.stopService() @@ -305,7 +304,7 @@ object V2RayServiceManager { if (mSubscription == null && v2rayPoint.isRunning && service.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_SPEED_ENABLED, false)) { - var last_zero_speed = false + var lastZeroSpeed = false val outboundTags = service.defaultDPreference.getPrefStringOrderedSet(AppConfig.PREF_CURR_CONFIG_OUTBOUND_TAGS, LinkedHashSet()) outboundTags.remove(TAG_DIRECT) @@ -325,8 +324,8 @@ object V2RayServiceManager { } val directUplink = v2rayPoint.queryStats(TAG_DIRECT, "uplink") val directDownlink = v2rayPoint.queryStats(TAG_DIRECT, "downlink") - val zero_speed = (proxyTotal == 0L && directUplink == 0L && directDownlink == 0L) - if (!zero_speed || !last_zero_speed) { + val zeroSpeed = (proxyTotal == 0L && directUplink == 0L && directDownlink == 0L) + if (!zeroSpeed || !lastZeroSpeed) { if (proxyTotal == 0L) { appendSpeedString(text, outboundTags.firstOrNull(), 0.0, 0.0) } @@ -334,7 +333,7 @@ object V2RayServiceManager { directDownlink / sinceLastQueryInSeconds) updateNotification(text.toString(), proxyTotal, directDownlink + directUplink) } - last_zero_speed = zero_speed + lastZeroSpeed = zeroSpeed lastQueryTime = queryTime } } @@ -342,7 +341,7 @@ object V2RayServiceManager { private fun appendSpeedString(text: StringBuilder, name: String?, up: Double, down: Double) { var n = name ?: "no tag" - n = n.substring(0, Math.min(n.length, 6)) + n = n.substring(0, min(n.length, 6)) text.append(n) for (i in n.length..6 step 2) { text.append("\t") @@ -356,8 +355,8 @@ object V2RayServiceManager { mSubscription?.unsubscribe() //stop queryStats mSubscription = null - val cf_name = service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "") - updateNotification(cf_name, 0, 0) + val cfName = service.defaultDPreference.getPrefString(AppConfig.PREF_CURR_CONFIG_NAME, "") + updateNotification(cfName, 0, 0) } } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt index 0567f634..fcbfd325 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt @@ -39,7 +39,6 @@ class V2RayVpnService : VpnService(), ServiceControl { .build() } - private val connectivity by lazy { getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager } private val defaultNetworkCallback by lazy @RequiresApi(Build.VERSION_CODES.P) { @@ -56,6 +55,7 @@ class V2RayVpnService : VpnService(), ServiceControl { } } } + private var listeningForDefaultNetwork = false override fun onCreate() { @@ -80,9 +80,9 @@ class V2RayVpnService : VpnService(), ServiceControl { V2RayServiceManager.cancelNotification() } - fun setup(parameters: String) { + private fun setup(parameters: String) { - val prepare = VpnService.prepare(this) + val prepare = prepare(this) if (prepare != null) { return } @@ -105,8 +105,8 @@ class V2RayVpnService : VpnService(), ServiceControl { if (it[1] == "::") { //not very elegant, should move Vpn setting in Kotlin, simplify go code builder.addRoute("2000::", 3) } else { - resources.getStringArray(R.array.bypass_private_ip_address).forEach { - val addr = it.split('/') + resources.getStringArray(R.array.bypass_private_ip_address).forEach { cidr -> + val addr = cidr.split('/') builder.addRoute(addr[0], addr[1].toInt()) } } @@ -147,9 +147,9 @@ class V2RayVpnService : VpnService(), ServiceControl { try { mInterface.close() } catch (ignored: Exception) { + // ignored } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { connectivity.requestNetwork(defaultNetworkRequest, defaultNetworkCallback) listeningForDefaultNetwork = true @@ -160,7 +160,7 @@ class V2RayVpnService : VpnService(), ServiceControl { sendFd() } - fun sendFd() { + private fun sendFd() { val fd = mInterface.fileDescriptor val path = File(Utils.packagePath(applicationContext), "sock_path").absolutePath @@ -168,7 +168,7 @@ class V2RayVpnService : VpnService(), ServiceControl { var tries = 0 while (true) try { Thread.sleep(50L shl tries) - Log.d(packageName, "sendFd tries: " + tries.toString()) + Log.d(packageName, "sendFd tries: $tries") LocalSocket().use { localSocket -> localSocket.connect(LocalSocketAddress(path, LocalSocketAddress.Namespace.FILESYSTEM)) localSocket.setFileDescriptorsForSend(arrayOf(fd)) @@ -194,11 +194,9 @@ class V2RayVpnService : VpnService(), ServiceControl { // val emptyInfo = VpnNetworkInfo() // val info = loadVpnNetworkInfo(configName, emptyInfo)!! + (lastNetworkInfo ?: emptyInfo) // saveVpnNetworkInfo(configName, info) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - if (listeningForDefaultNetwork) { - connectivity.unregisterNetworkCallback(defaultNetworkCallback) - listeningForDefaultNetwork = false - } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && listeningForDefaultNetwork) { + connectivity.unregisterNetworkCallback(defaultNetworkCallback) + listeningForDefaultNetwork = false } V2RayServiceManager.stopV2rayPoint() @@ -214,6 +212,7 @@ class V2RayVpnService : VpnService(), ServiceControl { try { mInterface.close() } catch (ignored: Exception) { + // ignored } } From ac668788b30bb409e297a8f6a375bdc53c580365 Mon Sep 17 00:00:00 2001 From: yuhan6665 <1588741+yuhan6665@users.noreply.github.com> Date: Fri, 4 Sep 2020 18:19:51 -0400 Subject: [PATCH 3/6] Add proxy only service --- V2rayNG/app/src/main/AndroidManifest.xml | 6 +++ .../ang/service/V2RayProxyOnlyService.kt | 47 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayProxyOnlyService.kt diff --git a/V2rayNG/app/src/main/AndroidManifest.xml b/V2rayNG/app/src/main/AndroidManifest.xml index 3d627cf7..96b61228 100644 --- a/V2rayNG/app/src/main/AndroidManifest.xml +++ b/V2rayNG/app/src/main/AndroidManifest.xml @@ -96,6 +96,12 @@ android:value="true" /> + + + Date: Fri, 4 Sep 2020 18:19:51 -0400 Subject: [PATCH 4/6] Make all toggle component run in daemon process This commit make the two process with clear role: - Daemon process will run proxy core, keep minimum configuration and long running in the background, support toggle on/off - Main process will have all UI, all servers configuration --- V2rayNG/app/src/main/AndroidManifest.xml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/V2rayNG/app/src/main/AndroidManifest.xml b/V2rayNG/app/src/main/AndroidManifest.xml index 96b61228..15c788d5 100644 --- a/V2rayNG/app/src/main/AndroidManifest.xml +++ b/V2rayNG/app/src/main/AndroidManifest.xml @@ -114,10 +114,11 @@ + android:name=".service.QSTileService" + android:icon="@drawable/ic_v" + android:label="@string/app_tile_name" + android:permission="android.permission.BIND_QUICK_SETTINGS_TILE" + android:process=":RunSoLibV2RayDaemon"> @@ -132,7 +133,8 @@ - + From 9271857b1e4864ebd523635fba562810fe5a8b2e Mon Sep 17 00:00:00 2001 From: yuhan6665 <1588741+yuhan6665@users.noreply.github.com> Date: Fri, 4 Sep 2020 18:19:51 -0400 Subject: [PATCH 5/6] Remove VPN related logic in toggle components --- V2rayNG/app/src/main/AndroidManifest.xml | 1 + .../com/v2ray/ang/receiver/WidgetProvider.kt | 34 ++++--- .../com/v2ray/ang/service/QSTileService.kt | 11 +-- .../v2ray/ang/service/V2RayServiceManager.kt | 3 +- .../com/v2ray/ang/ui/ScSwitchActivity.kt | 94 ++----------------- .../com/v2ray/ang/ui/SettingsActivity.kt | 4 +- .../main/kotlin/com/v2ray/ang/util/Utils.kt | 31 +----- .../src/main/res/values-zh-rCN/strings.xml | 2 +- .../src/main/res/values-zh-rTW/strings.xml | 2 +- V2rayNG/app/src/main/res/values/strings.xml | 2 +- 10 files changed, 40 insertions(+), 144 deletions(-) diff --git a/V2rayNG/app/src/main/AndroidManifest.xml b/V2rayNG/app/src/main/AndroidManifest.xml index 15c788d5..71ddbab5 100644 --- a/V2rayNG/app/src/main/AndroidManifest.xml +++ b/V2rayNG/app/src/main/AndroidManifest.xml @@ -110,6 +110,7 @@ + diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/WidgetProvider.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/WidgetProvider.kt index dfda98b9..0f79a90b 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/WidgetProvider.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/WidgetProvider.kt @@ -9,6 +9,7 @@ import android.content.Intent import android.widget.RemoteViews import com.v2ray.ang.R import com.v2ray.ang.AppConfig +import com.v2ray.ang.service.V2RayServiceManager import com.v2ray.ang.util.Utils class WidgetProvider : AppWidgetProvider() { @@ -17,21 +18,19 @@ class WidgetProvider : AppWidgetProvider() { */ override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { super.onUpdate(context, appWidgetManager, appWidgetIds) - - val isRunning = Utils.isServiceRun(context, "com.v2ray.ang.service.V2RayVpnService") - updateWidgetBackground(context, appWidgetManager, appWidgetIds, isRunning) + updateWidgetBackground(context, appWidgetManager, appWidgetIds, V2RayServiceManager.v2rayPoint.isRunning) } private fun updateWidgetBackground(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, isRunning: Boolean) { val remoteViews = RemoteViews(context.packageName, R.layout.widget_switch) val intent = Intent(context, WidgetProvider::class.java) - intent.setAction(AppConfig.BROADCAST_ACTION_WIDGET_CLICK) + intent.action = AppConfig.BROADCAST_ACTION_WIDGET_CLICK val pendingIntent = PendingIntent.getBroadcast(context, R.id.layout_switch, intent, PendingIntent.FLAG_UPDATE_CURRENT) remoteViews.setOnClickPendingIntent(R.id.layout_switch, pendingIntent) if (isRunning) { - remoteViews.setInt(R.id.layout_switch, "setBackgroundResource", R.drawable.ic_rounded_corner_theme); + remoteViews.setInt(R.id.layout_switch, "setBackgroundResource", R.drawable.ic_rounded_corner_theme) } else { - remoteViews.setInt(R.id.layout_switch, "setBackgroundResource", R.drawable.ic_rounded_corner_grey); + remoteViews.setInt(R.id.layout_switch, "setBackgroundResource", R.drawable.ic_rounded_corner_grey) } for (appWidgetId in appWidgetIds) { @@ -40,23 +39,28 @@ class WidgetProvider : AppWidgetProvider() { } /** - * 接收窗口小部件点击时发送的广播 + * 接收窗口小部件发送的广播 */ override fun onReceive(context: Context, intent: Intent) { super.onReceive(context, intent) if (AppConfig.BROADCAST_ACTION_WIDGET_CLICK == intent.action) { - - val isRunning = Utils.isServiceRun(context, "com.v2ray.ang.service.V2RayVpnService") - if (isRunning) { -// context.toast(R.string.toast_services_stop) + if (V2RayServiceManager.v2rayPoint.isRunning) { Utils.stopVService(context) } else { -// context.toast(R.string.toast_services_start) - Utils.startVService(context) + Utils.startVServiceFromToggle(context) } + } else if (AppConfig.BROADCAST_ACTION_ACTIVITY == intent.action) { val manager = AppWidgetManager.getInstance(context) - updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)), - !isRunning); + when (intent.getIntExtra("key", 0)) { + AppConfig.MSG_STATE_RUNNING, AppConfig.MSG_STATE_START_SUCCESS -> { + updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)), + true) + } + AppConfig.MSG_STATE_NOT_RUNNING, AppConfig.MSG_STATE_START_FAILURE, AppConfig.MSG_STATE_STOP_SUCCESS -> { + updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)), + false) + } + } } } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/QSTileService.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/QSTileService.kt index cbb82425..3c9320e9 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/QSTileService.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/QSTileService.kt @@ -6,7 +6,6 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.graphics.drawable.Icon -import android.net.VpnService import android.os.Build import android.service.quicksettings.Tile import android.service.quicksettings.TileService @@ -15,7 +14,6 @@ import com.v2ray.ang.R import com.v2ray.ang.extension.defaultDPreference import com.v2ray.ang.util.MessageUtil import com.v2ray.ang.util.Utils -import org.jetbrains.anko.toast import java.lang.ref.SoftReference @@ -33,7 +31,6 @@ class QSTileService : TileService() { qsTile?.icon = Icon.createWithResource(applicationContext, R.drawable.ic_v) } - qsTile?.updateTile() } @@ -56,11 +53,7 @@ class QSTileService : TileService() { super.onClick() when (qsTile.state) { Tile.STATE_INACTIVE -> { - val intent = VpnService.prepare(this) - if (intent == null) - if (!Utils.startVService(this)) { - toast(R.string.app_tile_first_use) - } + Utils.startVServiceFromToggle(this) } Tile.STATE_ACTIVE -> { Utils.stopVService(this) @@ -93,4 +86,4 @@ class QSTileService : TileService() { } } } -} \ No newline at end of file +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt index 07907e82..0fcb708a 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt @@ -24,6 +24,7 @@ import com.v2ray.ang.util.MessageUtil import com.v2ray.ang.util.Utils import go.Seq import libv2ray.Libv2ray +import libv2ray.V2RayPoint import libv2ray.V2RayVPNServiceSupportsSet import rx.Observable import rx.Subscription @@ -36,7 +37,7 @@ object V2RayServiceManager { private const val NOTIFICATION_PENDING_INTENT_STOP_V2RAY = 1 private const val NOTIFICATION_ICON_THRESHOLD = 3000 - private val v2rayPoint = Libv2ray.newV2RayPoint(V2RayCallback()) + val v2rayPoint: V2RayPoint = Libv2ray.newV2RayPoint(V2RayCallback()) private val mMsgReceive = ReceiveMessageHandler() var serviceControl: SoftReference? = null diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScSwitchActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScSwitchActivity.kt index 5b023055..3d3101ef 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScSwitchActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScSwitchActivity.kt @@ -1,104 +1,22 @@ package com.v2ray.ang.ui -import android.content.* -import android.net.VpnService import com.v2ray.ang.R import com.v2ray.ang.util.Utils import android.os.Bundle -import com.v2ray.ang.AppConfig -import com.v2ray.ang.util.MessageUtil -import java.lang.ref.SoftReference -import android.content.IntentFilter -import kotlinx.android.synthetic.main.activity_main.* -import rx.Observable -import rx.android.schedulers.AndroidSchedulers -import java.util.concurrent.TimeUnit +import com.v2ray.ang.service.V2RayServiceManager class ScSwitchActivity : BaseActivity() { - companion object { - private const val REQUEST_CODE_VPN_PREPARE = 0 - } - - var isRunning = false - set(value) { - field = value - if (value) { - Utils.stopVService(this) - } else { - val intent = VpnService.prepare(this) - if (intent == null) { - Utils.startVService(this) - } else { - startActivityForResult(intent, REQUEST_CODE_VPN_PREPARE) - } - } - finishActivity() - } - - fun finishActivity() { - try { - Observable.timer(5000, TimeUnit.MILLISECONDS) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { - finish() - } - } catch (e: Exception) { - } - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) moveTaskToBack(true) setContentView(R.layout.activity_none) - val isRunning = Utils.isServiceRun(this, "com.v2ray.ang.service.V2RayVpnService") - if (isRunning) { - //Utils.stopVService(this) - mMsgReceive = ReceiveMessageHandler(this@ScSwitchActivity) - registerReceiver(mMsgReceive, IntentFilter(AppConfig.BROADCAST_ACTION_ACTIVITY)) - MessageUtil.sendMsg2Service(this, AppConfig.MSG_REGISTER_CLIENT, "") - + if (V2RayServiceManager.v2rayPoint.isRunning) { + Utils.stopVService(this) } else { - Utils.startVService(this) - finishActivity() + Utils.startVServiceFromToggle(this) } + finish() } - - override fun onStop() { - super.onStop() - if (mMsgReceive != null) { - unregisterReceiver(mMsgReceive) - mMsgReceive = null - } - } - - private var mMsgReceive: BroadcastReceiver? = null - - private class ReceiveMessageHandler(activity: ScSwitchActivity) : BroadcastReceiver() { - internal var mReference: SoftReference = SoftReference(activity) - override fun onReceive(ctx: Context?, intent: Intent?) { - val activity = mReference.get() - when (intent?.getIntExtra("key", 0)) { - AppConfig.MSG_STATE_RUNNING -> { - activity?.isRunning = true - } - AppConfig.MSG_STATE_NOT_RUNNING -> { - activity?.isRunning = false - } -// AppConfig.MSG_STATE_START_SUCCESS -> { -// activity?.toast(R.string.toast_services_success) -// activity?.isRunning = true -// } -// AppConfig.MSG_STATE_START_FAILURE -> { -// activity?.toast(R.string.toast_services_failure) -// activity?.isRunning = false -// } -// AppConfig.MSG_STATE_STOP_SUCCESS -> { -// activity?.isRunning = false -// } - } - } - } - -} \ No newline at end of file +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt index f83ae661..0bf13dff 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt @@ -88,7 +88,7 @@ class SettingsActivity : BaseActivity() { } private fun isRunning(): Boolean { - return Utils.isServiceRun(activity, "com.v2ray.ang.service.V2RayVpnService") + return false //TODO no point of adding logic now since Settings will be changed soon } override fun onCreate(savedInstanceState: Bundle?) { @@ -247,4 +247,4 @@ class SettingsActivity : BaseActivity() { } } -} \ No newline at end of file +} diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt index 11bb67ce..b61172cb 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt @@ -11,7 +11,6 @@ import com.google.zxing.qrcode.QRCodeWriter import com.google.zxing.EncodeHintType import java.util.* import kotlin.collections.HashMap -import android.app.ActivityManager import android.content.ClipData import android.content.Intent import android.net.Uri @@ -273,32 +272,12 @@ object Utils { return false } - - /** - * 判断服务是否后台运行 - - * @param context - * * Context - * * - * @param className - * * 判断的服务名字 - * * - * @return true 在运行 false 不在运行 - */ - fun isServiceRun(context: Context, className: String): Boolean { - var isRun = false - val activityManager = context - .getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager - val serviceList = activityManager - .getRunningServices(999) - val size = serviceList.size - for (i in 0..size - 1) { - if (serviceList[i].service.className == className) { - isRun = true - break - } + fun startVServiceFromToggle(context: Context): Boolean { + val result = startVService(context) + if (!result) { + context.toast(R.string.app_tile_first_use) } - return isRun + return result } /** diff --git a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml index b08a711a..fe574e6b 100644 --- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -3,7 +3,7 @@ v2rayNG 开关 开关 - 初次使用此功能请先用APP激活VPN + 初次使用此功能请先用APP添加配置 Open navigation drawer Close navigation drawer diff --git a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml index d7310a34..6f69a937 100644 --- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -3,7 +3,7 @@ v2rayNG 切換 切換 - 首次使用此功能,請使用此應用程式來啟用 VPN + 首次使用此功能,請使用此應用程式新增組態 Open navigation drawer Close navigation drawer diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index 129d0264..18e712cf 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -3,7 +3,7 @@ v2rayNG Switch Switch - First use of this feature, please use the app to activate VPN + First use of this feature, please use the app to add server Open navigation drawer Close navigation drawer From bebc6fea1374de50796aa20e4645b20bf4fbcbc6 Mon Sep 17 00:00:00 2001 From: yuhan6665 <1588741+yuhan6665@users.noreply.github.com> Date: Fri, 4 Sep 2020 18:19:51 -0400 Subject: [PATCH 6/6] Add mode in settings --- V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt | 2 ++ .../com/v2ray/ang/service/V2RayServiceManager.kt | 8 ++++++-- .../src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt | 5 ++++- .../app/src/main/kotlin/com/v2ray/ang/util/Utils.kt | 2 +- V2rayNG/app/src/main/res/values-zh-rCN/strings.xml | 1 + V2rayNG/app/src/main/res/values-zh-rTW/strings.xml | 1 + V2rayNG/app/src/main/res/values/arrays.xml | 5 +++++ V2rayNG/app/src/main/res/values/strings.xml | 1 + V2rayNG/app/src/main/res/xml/pref_settings.xml | 10 +++++++++- 9 files changed, 30 insertions(+), 5 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt index eff2c8c2..2956e477 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt @@ -13,6 +13,8 @@ object AppConfig { const val PREF_CURR_CONFIG_DOMAIN = "pref_v2ray_config_domain" const val PREF_CURR_CONFIG_OUTBOUND_TAGS = "pref_v2ray_config_outbound_tags" const val PREF_INAPP_BUY_IS_PREMIUM = "pref_inapp_buy_is_premium" + const val PREF_MODE = "pref_mode" + const val VMESS_PROTOCOL: String = "vmess://" const val SS_PROTOCOL: String = "ss://" const val SOCKS_PROTOCOL: String = "socks://" diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt index 0fcb708a..a85e8467 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt @@ -56,8 +56,12 @@ object V2RayServiceManager { private var mSubscription: Subscription? = null private var mNotificationManager: NotificationManager? = null - fun startV2Ray(context: Context) { - val intent = Intent(context.applicationContext, V2RayVpnService::class.java) + fun startV2Ray(context: Context, mode: String) { + val intent = if (mode == "VPN") { + Intent(context.applicationContext, V2RayVpnService::class.java) + } else { + Intent(context.applicationContext, V2RayProxyOnlyService::class.java) + } if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) { context.startForegroundService(intent) } else { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt index 080858a7..b09b2a0f 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt @@ -28,6 +28,7 @@ import android.support.v7.app.ActionBarDrawerToggle import android.support.v7.widget.helper.ItemTouchHelper import android.util.Log import com.v2ray.ang.dto.EConfigType +import com.v2ray.ang.extension.defaultDPreference //import com.v2ray.ang.InappBuyActivity import rx.Observable import rx.android.schedulers.AndroidSchedulers @@ -71,13 +72,15 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList fab.setOnClickListener { if (isRunning) { Utils.stopVService(this) - } else { + } else if (defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN") == "VPN") { val intent = VpnService.prepare(this) if (intent == null) { startV2Ray() } else { startActivityForResult(intent, REQUEST_CODE_VPN_PREPARE) } + } else { + startV2Ray() } } layout_test.setOnClickListener { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt index b61172cb..efcdde4d 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt @@ -300,7 +300,7 @@ object Utils { return false } } - V2RayServiceManager.startV2Ray(context) + V2RayServiceManager.startV2Ray(context, context.v2RayApplication.defaultDPreference.getPrefString(AppConfig.PREF_MODE, "VPN")) return true } else { return false diff --git a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml index fe574e6b..592c2fb8 100644 --- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -123,6 +123,7 @@ 推广 一些推广,点击查看详情(捐赠可去除) + 模式 版本 初始化错误: diff --git a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml index 6f69a937..14714e24 100644 --- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -125,6 +125,7 @@ 推廣 一些推廣,點擊查看詳情(捐款可去除) + 模式 版本 錯誤設定: diff --git a/V2rayNG/app/src/main/res/values/arrays.xml b/V2rayNG/app/src/main/res/values/arrays.xml index 11dfa77b..b75fae68 100644 --- a/V2rayNG/app/src/main/res/values/arrays.xml +++ b/V2rayNG/app/src/main/res/values/arrays.xml @@ -67,6 +67,11 @@ IPOnDemand + + VPN + Proxy only + + 0.0.0.0/5 diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index 18e712cf..ee72e193 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -125,6 +125,7 @@ Promotion Promotion,click for details(Donation can be removed) + Mode Version Error Setup: diff --git a/V2rayNG/app/src/main/res/xml/pref_settings.xml b/V2rayNG/app/src/main/res/xml/pref_settings.xml index 6dcf3c16..19aa1e3f 100644 --- a/V2rayNG/app/src/main/res/xml/pref_settings.xml +++ b/V2rayNG/app/src/main/res/xml/pref_settings.xml @@ -85,6 +85,14 @@ android:key="pref_http_port" android:summary="10809" android:title="@string/title_pref_http_port" /> + + @@ -110,4 +118,4 @@ android:key="pref_version" android:title="@string/title_pref_version" /> - \ No newline at end of file +