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
[![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)
[![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)
......
......@@ -26,7 +26,7 @@ fun Project.setupCommon() {
android.apply {
compileSdkVersion(30)
defaultConfig {
minSdkVersion(21)
minSdkVersion(23)
targetSdkVersion(30)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
......
......@@ -14,7 +14,7 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<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"
android:required="false"/>
......@@ -29,7 +29,6 @@
android:icon="@mipmap/ic_launcher"
android:allowBackup="true"
android:autoRevokePermissions="allowed"
android:backupAgent="com.github.shadowsocks.ConfigBackupHelper"
android:extractNativeLibs="true"
android:fullBackupContent="@xml/backup_descriptor"
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 {
val success = socket.ancillaryFileDescriptors!!.single()!!.use { fd ->
underlyingNetwork.let { network ->
if (network != null) try {
DnsResolverCompat.bindSocket(network, fd)
network.bindSocket(fd)
return@let true
} catch (e: IOException) {
when ((e.cause as? ErrnoException)?.errno) {
......@@ -85,9 +85,6 @@ class VpnService : BaseVpnService(), BaseService.Interface {
else -> Timber.w(e)
}
return@let false
} catch (e: ReflectiveOperationException) {
check(Build.VERSION.SDK_INT < 23)
Timber.w(e)
}
protect(fd.int)
}
......@@ -114,7 +111,7 @@ class VpnService : BaseVpnService(), BaseService.Interface {
private var underlyingNetwork: Network? = null
set(value) {
field = value
if (active && Build.VERSION.SDK_INT >= 22) setUnderlyingNetworks(underlyingNetworks)
if (active) setUnderlyingNetworks(underlyingNetworks)
}
private val underlyingNetworks get() =
// clearing underlyingNetworks makes Android 9 consider the network to be metered
......@@ -207,10 +204,8 @@ class VpnService : BaseVpnService(), BaseService.Interface {
metered = profile.metered
active = true // possible race condition here?
if (Build.VERSION.SDK_INT >= 22) {
builder.setUnderlyingNetworks(underlyingNetworks)
if (Build.VERSION.SDK_INT >= 29) builder.setMetered(metered)
}
builder.setUnderlyingNetworks(underlyingNetworks)
if (Build.VERSION.SDK_INT >= 29) builder.setMetered(metered)
val conn = builder.establish() ?: throw NullConnectionException()
this.conn = conn
......
......@@ -20,18 +20,14 @@
package com.github.shadowsocks.net
import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.net.DnsResolver
import android.net.Network
import android.os.Build
import android.os.CancellationSignal
import android.system.ErrnoException
import com.github.shadowsocks.Core
import com.github.shadowsocks.utils.int
import kotlinx.coroutines.*
import org.xbill.DNS.*
import java.io.FileDescriptor
import java.io.IOException
import java.net.Inet4Address
import java.net.Inet6Address
......@@ -47,12 +43,10 @@ sealed class DnsResolverCompat {
when (Build.VERSION.SDK_INT) {
in 29..Int.MAX_VALUE -> DnsResolverCompat29
in 23 until 29 -> DnsResolverCompat23
in 21 until 23 -> DnsResolverCompat21()
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 resolveOnActiveNetwork(host: String) = instance.resolveOnActiveNetwork(host)
override suspend fun resolveRaw(network: Network, query: ByteArray) = instance.resolveRaw(network, query)
......@@ -75,28 +69,12 @@ sealed class DnsResolverCompat {
}
@Throws(IOException::class)
abstract fun bindSocket(network: Network, socket: FileDescriptor)
abstract suspend fun resolve(network: Network, host: String): Array<InetAddress>
abstract suspend fun resolveOnActiveNetwork(host: String): Array<InetAddress>
abstract suspend fun resolveRaw(network: Network, query: ByteArray): ByteArray
abstract suspend fun resolveRawOnActiveNetwork(query: ByteArray): ByteArray
@SuppressLint("PrivateApi")
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()
}
private object DnsResolverCompat23 : DnsResolverCompat() {
/**
* This dispatcher is used for noncancellable possibly-forever-blocking operations in network IO.
*
......@@ -161,11 +139,6 @@ sealed class DnsResolverCompat {
resolveRaw(query, false, this::resolveOnActiveNetwork)
}
@TargetApi(23)
private object DnsResolverCompat23 : DnsResolverCompat21() {
override fun bindSocket(network: Network, socket: FileDescriptor) = network.bindSocket(socket)
}
@TargetApi(29)
private object DnsResolverCompat29 : DnsResolverCompat(), Executor {
/**
......@@ -173,8 +146,6 @@ sealed class DnsResolverCompat {
*/
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")
override suspend fun resolve(network: Network, host: String): Array<InetAddress> {
......
Subproject commit b0569e56ddabc33f8a40f64825676879eafa08c9
Subproject commit 5324ab7829f9dd3c4c5390308a3418917ba27d29
......@@ -4,8 +4,8 @@
tools:ignore="MissingLeanbackSupport">
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<uses-permission-sdk-23 android:name="android.permission.CAMERA" />
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.touchscreen"
android:required="false"/>
......
......@@ -82,7 +82,6 @@ class ScannerActivity : AppCompatActivity(), ImageAnalysis.Analyzer {
override fun onCreate(savedInstanceState: Bundle?) {
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")
setContentView(R.layout.layout_scanner)
ListHolderListener.setup(this)
......
......@@ -24,7 +24,6 @@ import android.annotation.SuppressLint
import android.content.DialogInterface
import android.content.Intent
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.text.Editable
......@@ -494,15 +493,12 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
val window = activity.window
// In the end material_grey_100 is used for background, see AppCompatDrawableManager (very complicated)
// for dark mode, it's roughly 850? (#303030)
window.statusBarColor = ContextCompat.getColor(activity, when {
resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES ->
android.R.color.black
Build.VERSION.SDK_INT >= 23 -> {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
R.color.material_grey_300
}
else -> R.color.material_grey_600
})
val colorId = if (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK !=
Configuration.UI_MODE_NIGHT_YES) {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
R.color.material_grey_300
} else android.R.color.black
window.statusBarColor = ContextCompat.getColor(activity, colorId)
activity.menuInflater.inflate(R.menu.custom_rules_selection, menu)
toolbar.touchscreenBlocksFocus = 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