Unverified Commit 3f20838f authored by Mygod's avatar Mygod Committed by GitHub

Merge pull request #2563 from Mygod/min-api-23

Drop support for Android Lollipop
parents 0d75659c 4bff3fb6
## [Shadowsocks](https://shadowsocks.org) for Android ## [Shadowsocks](https://shadowsocks.org) for Android
[![CircleCI](https://circleci.com/gh/shadowsocks/shadowsocks-android.svg?style=shield)](https://circleci.com/gh/shadowsocks/shadowsocks-android) [![CircleCI](https://circleci.com/gh/shadowsocks/shadowsocks-android.svg?style=shield)](https://circleci.com/gh/shadowsocks/shadowsocks-android)
[![API](https://img.shields.io/badge/API-21%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=21) [![API](https://img.shields.io/badge/API-23%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=23)
[![Releases](https://img.shields.io/github/downloads/shadowsocks/shadowsocks-android/total.svg)](https://github.com/shadowsocks/shadowsocks-android/releases) [![Releases](https://img.shields.io/github/downloads/shadowsocks/shadowsocks-android/total.svg)](https://github.com/shadowsocks/shadowsocks-android/releases)
[![Language: Kotlin](https://img.shields.io/github/languages/top/shadowsocks/shadowsocks-android.svg)](https://github.com/shadowsocks/shadowsocks-android/search?l=kotlin) [![Language: Kotlin](https://img.shields.io/github/languages/top/shadowsocks/shadowsocks-android.svg)](https://github.com/shadowsocks/shadowsocks-android/search?l=kotlin)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/1a21d48d466644cdbcb57a1889abea5b)](https://www.codacy.com/app/shadowsocks/shadowsocks-android?utm_source=github.com&utm_medium=referral&utm_content=shadowsocks/shadowsocks-android&utm_campaign=Badge_Grade) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/1a21d48d466644cdbcb57a1889abea5b)](https://www.codacy.com/app/shadowsocks/shadowsocks-android?utm_source=github.com&utm_medium=referral&utm_content=shadowsocks/shadowsocks-android&utm_campaign=Badge_Grade)
......
...@@ -26,7 +26,7 @@ fun Project.setupCommon() { ...@@ -26,7 +26,7 @@ fun Project.setupCommon() {
android.apply { android.apply {
compileSdkVersion(30) compileSdkVersion(30)
defaultConfig { defaultConfig {
minSdkVersion(21) minSdkVersion(23)
targetSdkVersion(30) targetSdkVersion(30)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission-sdk-23 android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.software.leanback" <uses-feature android:name="android.software.leanback"
android:required="false"/> android:required="false"/>
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,6 @@
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:allowBackup="true" android:allowBackup="true"
android:autoRevokePermissions="allowed" android:autoRevokePermissions="allowed"
android:backupAgent="com.github.shadowsocks.ConfigBackupHelper"
android:extractNativeLibs="true" android:extractNativeLibs="true"
android:fullBackupContent="@xml/backup_descriptor" android:fullBackupContent="@xml/backup_descriptor"
android:fullBackupOnly="true" android:fullBackupOnly="true"
......
/*******************************************************************************
* *
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
package com.github.shadowsocks
import android.app.backup.BackupAgentHelper
import android.app.backup.FileBackupHelper
import com.github.shadowsocks.utils.Key
@Deprecated("Only used in API level < 23. For 6.0+, Auto Backup for Apps is used.")
class ConfigBackupHelper : BackupAgentHelper() {
override fun onCreate() = addHelper("com.github.shadowsocks.database.profile", FileBackupHelper(this,
"../databases/" + Key.DB_PROFILE, "../databases/" + Key.DB_PUBLIC))
}
...@@ -76,7 +76,7 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -76,7 +76,7 @@ class VpnService : BaseVpnService(), BaseService.Interface {
val success = socket.ancillaryFileDescriptors!!.single()!!.use { fd -> val success = socket.ancillaryFileDescriptors!!.single()!!.use { fd ->
underlyingNetwork.let { network -> underlyingNetwork.let { network ->
if (network != null) try { if (network != null) try {
DnsResolverCompat.bindSocket(network, fd) network.bindSocket(fd)
return@let true return@let true
} catch (e: IOException) { } catch (e: IOException) {
when ((e.cause as? ErrnoException)?.errno) { when ((e.cause as? ErrnoException)?.errno) {
...@@ -85,9 +85,6 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -85,9 +85,6 @@ class VpnService : BaseVpnService(), BaseService.Interface {
else -> Timber.w(e) else -> Timber.w(e)
} }
return@let false return@let false
} catch (e: ReflectiveOperationException) {
check(Build.VERSION.SDK_INT < 23)
Timber.w(e)
} }
protect(fd.int) protect(fd.int)
} }
...@@ -114,7 +111,7 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -114,7 +111,7 @@ class VpnService : BaseVpnService(), BaseService.Interface {
private var underlyingNetwork: Network? = null private var underlyingNetwork: Network? = null
set(value) { set(value) {
field = value field = value
if (active && Build.VERSION.SDK_INT >= 22) setUnderlyingNetworks(underlyingNetworks) if (active) setUnderlyingNetworks(underlyingNetworks)
} }
private val underlyingNetworks get() = private val underlyingNetworks get() =
// clearing underlyingNetworks makes Android 9 consider the network to be metered // clearing underlyingNetworks makes Android 9 consider the network to be metered
...@@ -207,10 +204,8 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -207,10 +204,8 @@ class VpnService : BaseVpnService(), BaseService.Interface {
metered = profile.metered metered = profile.metered
active = true // possible race condition here? active = true // possible race condition here?
if (Build.VERSION.SDK_INT >= 22) { builder.setUnderlyingNetworks(underlyingNetworks)
builder.setUnderlyingNetworks(underlyingNetworks) if (Build.VERSION.SDK_INT >= 29) builder.setMetered(metered)
if (Build.VERSION.SDK_INT >= 29) builder.setMetered(metered)
}
val conn = builder.establish() ?: throw NullConnectionException() val conn = builder.establish() ?: throw NullConnectionException()
this.conn = conn this.conn = conn
......
...@@ -20,18 +20,14 @@ ...@@ -20,18 +20,14 @@
package com.github.shadowsocks.net package com.github.shadowsocks.net
import android.annotation.SuppressLint
import android.annotation.TargetApi import android.annotation.TargetApi
import android.net.DnsResolver import android.net.DnsResolver
import android.net.Network import android.net.Network
import android.os.Build import android.os.Build
import android.os.CancellationSignal import android.os.CancellationSignal
import android.system.ErrnoException
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.utils.int
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.xbill.DNS.* import org.xbill.DNS.*
import java.io.FileDescriptor
import java.io.IOException import java.io.IOException
import java.net.Inet4Address import java.net.Inet4Address
import java.net.Inet6Address import java.net.Inet6Address
...@@ -47,12 +43,10 @@ sealed class DnsResolverCompat { ...@@ -47,12 +43,10 @@ sealed class DnsResolverCompat {
when (Build.VERSION.SDK_INT) { when (Build.VERSION.SDK_INT) {
in 29..Int.MAX_VALUE -> DnsResolverCompat29 in 29..Int.MAX_VALUE -> DnsResolverCompat29
in 23 until 29 -> DnsResolverCompat23 in 23 until 29 -> DnsResolverCompat23
in 21 until 23 -> DnsResolverCompat21()
else -> error("Unsupported API level") else -> error("Unsupported API level")
} }
} }
override fun bindSocket(network: Network, socket: FileDescriptor) = instance.bindSocket(network, socket)
override suspend fun resolve(network: Network, host: String) = instance.resolve(network, host) override suspend fun resolve(network: Network, host: String) = instance.resolve(network, host)
override suspend fun resolveOnActiveNetwork(host: String) = instance.resolveOnActiveNetwork(host) override suspend fun resolveOnActiveNetwork(host: String) = instance.resolveOnActiveNetwork(host)
override suspend fun resolveRaw(network: Network, query: ByteArray) = instance.resolveRaw(network, query) override suspend fun resolveRaw(network: Network, query: ByteArray) = instance.resolveRaw(network, query)
...@@ -75,28 +69,12 @@ sealed class DnsResolverCompat { ...@@ -75,28 +69,12 @@ sealed class DnsResolverCompat {
} }
@Throws(IOException::class) @Throws(IOException::class)
abstract fun bindSocket(network: Network, socket: FileDescriptor)
abstract suspend fun resolve(network: Network, host: String): Array<InetAddress> abstract suspend fun resolve(network: Network, host: String): Array<InetAddress>
abstract suspend fun resolveOnActiveNetwork(host: String): Array<InetAddress> abstract suspend fun resolveOnActiveNetwork(host: String): Array<InetAddress>
abstract suspend fun resolveRaw(network: Network, query: ByteArray): ByteArray abstract suspend fun resolveRaw(network: Network, query: ByteArray): ByteArray
abstract suspend fun resolveRawOnActiveNetwork(query: ByteArray): ByteArray abstract suspend fun resolveRawOnActiveNetwork(query: ByteArray): ByteArray
@SuppressLint("PrivateApi") private object DnsResolverCompat23 : DnsResolverCompat() {
private open class DnsResolverCompat21 : DnsResolverCompat() {
private val bindSocketToNetwork by lazy {
Class.forName("android.net.NetworkUtils").getDeclaredMethod(
"bindSocketToNetwork", Int::class.java, Int::class.java)
}
private val netId by lazy { Network::class.java.getDeclaredField("netId") }
@SuppressLint("NewApi")
override fun bindSocket(network: Network, socket: FileDescriptor) {
val netId = netId.get(network)!!
val err = bindSocketToNetwork.invoke(null, socket.int, netId) as Int
if (err == 0) return
val message = "Binding socket to network $netId"
throw ErrnoException(message, -err).rethrowAsSocketException()
}
/** /**
* This dispatcher is used for noncancellable possibly-forever-blocking operations in network IO. * This dispatcher is used for noncancellable possibly-forever-blocking operations in network IO.
* *
...@@ -161,11 +139,6 @@ sealed class DnsResolverCompat { ...@@ -161,11 +139,6 @@ sealed class DnsResolverCompat {
resolveRaw(query, false, this::resolveOnActiveNetwork) resolveRaw(query, false, this::resolveOnActiveNetwork)
} }
@TargetApi(23)
private object DnsResolverCompat23 : DnsResolverCompat21() {
override fun bindSocket(network: Network, socket: FileDescriptor) = network.bindSocket(socket)
}
@TargetApi(29) @TargetApi(29)
private object DnsResolverCompat29 : DnsResolverCompat(), Executor { private object DnsResolverCompat29 : DnsResolverCompat(), Executor {
/** /**
...@@ -173,8 +146,6 @@ sealed class DnsResolverCompat { ...@@ -173,8 +146,6 @@ sealed class DnsResolverCompat {
*/ */
override fun execute(command: Runnable) = command.run() override fun execute(command: Runnable) = command.run()
override fun bindSocket(network: Network, socket: FileDescriptor) = network.bindSocket(socket)
private val activeNetwork get() = Core.connectivity.activeNetwork ?: throw IOException("no network") private val activeNetwork get() = Core.connectivity.activeNetwork ?: throw IOException("no network")
override suspend fun resolve(network: Network, host: String): Array<InetAddress> { override suspend fun resolve(network: Network, host: String): Array<InetAddress> {
......
Subproject commit b0569e56ddabc33f8a40f64825676879eafa08c9 Subproject commit 5324ab7829f9dd3c4c5390308a3418917ba27d29
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
tools:ignore="MissingLeanbackSupport"> tools:ignore="MissingLeanbackSupport">
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" /> tools:ignore="QueryAllPackagesPermission" />
<uses-permission-sdk-23 android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.touchscreen" <uses-feature android:name="android.hardware.touchscreen"
android:required="false"/> android:required="false"/>
......
...@@ -82,7 +82,6 @@ class ScannerActivity : AppCompatActivity(), ImageAnalysis.Analyzer { ...@@ -82,7 +82,6 @@ class ScannerActivity : AppCompatActivity(), ImageAnalysis.Analyzer {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT < 23) return startImport() // we show no love to lollipop
if (Build.VERSION.SDK_INT >= 25) getSystemService<ShortcutManager>()!!.reportShortcutUsed("scan") if (Build.VERSION.SDK_INT >= 25) getSystemService<ShortcutManager>()!!.reportShortcutUsed("scan")
setContentView(R.layout.layout_scanner) setContentView(R.layout.layout_scanner)
ListHolderListener.setup(this) ListHolderListener.setup(this)
......
...@@ -24,7 +24,6 @@ import android.annotation.SuppressLint ...@@ -24,7 +24,6 @@ import android.annotation.SuppressLint
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.text.Editable import android.text.Editable
...@@ -494,15 +493,12 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener, ...@@ -494,15 +493,12 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
val window = activity.window val window = activity.window
// In the end material_grey_100 is used for background, see AppCompatDrawableManager (very complicated) // In the end material_grey_100 is used for background, see AppCompatDrawableManager (very complicated)
// for dark mode, it's roughly 850? (#303030) // for dark mode, it's roughly 850? (#303030)
window.statusBarColor = ContextCompat.getColor(activity, when { val colorId = if (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK !=
resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES -> Configuration.UI_MODE_NIGHT_YES) {
android.R.color.black window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
Build.VERSION.SDK_INT >= 23 -> { R.color.material_grey_300
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR } else android.R.color.black
R.color.material_grey_300 window.statusBarColor = ContextCompat.getColor(activity, colorId)
}
else -> R.color.material_grey_600
})
activity.menuInflater.inflate(R.menu.custom_rules_selection, menu) activity.menuInflater.inflate(R.menu.custom_rules_selection, menu)
toolbar.touchscreenBlocksFocus = true toolbar.touchscreenBlocksFocus = true
return true return true
......
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