Commit 8e2c5812 authored by Mygod's avatar Mygod

Migrate to open source Firebase crashlytics

Fixes #2439.
parent 6cb488a9
...@@ -22,9 +22,6 @@ buildscript { ...@@ -22,9 +22,6 @@ buildscript {
repositories { repositories {
google() google()
jcenter() jcenter()
maven {
url 'https://maven.fabric.io/public'
}
maven { maven {
url "https://plugins.gradle.org/m2/" url "https://plugins.gradle.org/m2/"
} }
...@@ -34,10 +31,10 @@ buildscript { ...@@ -34,10 +31,10 @@ buildscript {
classpath 'com.android.tools.build:gradle:4.0.0-beta04' classpath 'com.android.tools.build:gradle:4.0.0-beta04'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.28.0' classpath 'com.github.ben-manes:gradle-versions-plugin:0.28.0'
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.2' classpath 'com.google.android.gms:oss-licenses-plugin:0.10.2'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0-beta04'
classpath 'com.google.gms:google-services:4.3.3' classpath 'com.google.gms:google-services:4.3.3'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.11.1' classpath 'com.vanniktech:gradle-maven-publish-plugin:0.11.1'
classpath 'gradle.plugin.org.mozilla.rust-android-gradle:plugin:0.8.3' classpath 'gradle.plugin.org.mozilla.rust-android-gradle:plugin:0.8.3'
classpath 'io.fabric.tools:gradle:1.31.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
} }
} }
......
...@@ -82,11 +82,12 @@ dependencies { ...@@ -82,11 +82,12 @@ dependencies {
api "androidx.room:room-runtime:$roomVersion" api "androidx.room:room-runtime:$roomVersion"
api "androidx.work:work-runtime-ktx:$workVersion" api "androidx.work:work-runtime-ktx:$workVersion"
api "androidx.work:work-gcm:$workVersion" api "androidx.work:work-gcm:$workVersion"
api 'com.crashlytics.sdk.android:crashlytics:2.10.1'
api 'com.google.android.gms:play-services-oss-licenses:17.0.0' api 'com.google.android.gms:play-services-oss-licenses:17.0.0'
api 'com.google.code.gson:gson:2.8.6' api 'com.google.code.gson:gson:2.8.6'
api 'com.google.firebase:firebase-analytics:17.3.0' api 'com.google.firebase:firebase-analytics-ktx:17.3.0'
api 'com.google.firebase:firebase-config:19.1.3' api 'com.google.firebase:firebase-config-ktx:19.1.3'
api 'com.google.firebase:firebase-crashlytics:17.0.0-beta04'
api 'com.jakewharton.timber:timber:4.7.1'
api 'dnsjava:dnsjava:3.0.2' api 'dnsjava:dnsjava:3.0.2'
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion" api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
api "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$coroutinesVersion" api "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$coroutinesVersion"
......
...@@ -29,35 +29,38 @@ import android.content.pm.PackageManager ...@@ -29,35 +29,38 @@ import android.content.pm.PackageManager
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.os.Build import android.os.Build
import android.os.UserManager import android.os.UserManager
import android.util.Log
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.work.Configuration import androidx.work.Configuration
import androidx.work.WorkManager import androidx.work.WorkManager
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.acl.Acl import com.github.shadowsocks.acl.Acl
import com.github.shadowsocks.aidl.ShadowsocksConnection import com.github.shadowsocks.aidl.ShadowsocksConnection
import com.github.shadowsocks.core.BuildConfig
import com.github.shadowsocks.core.R import com.github.shadowsocks.core.R
import com.github.shadowsocks.database.Profile import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.subscription.SubscriptionService import com.github.shadowsocks.subscription.SubscriptionService
import com.github.shadowsocks.utils.* import com.github.shadowsocks.utils.Action
import com.google.firebase.FirebaseApp import com.github.shadowsocks.utils.DeviceStorageApp
import com.google.firebase.analytics.FirebaseAnalytics import com.github.shadowsocks.utils.DirectBoot
import io.fabric.sdk.android.Fabric import com.github.shadowsocks.utils.Key
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.firebase.ktx.Firebase
import com.google.firebase.ktx.initialize
import kotlinx.coroutines.DEBUG_PROPERTY_NAME import kotlinx.coroutines.DEBUG_PROPERTY_NAME
import kotlinx.coroutines.DEBUG_PROPERTY_VALUE_ON import kotlinx.coroutines.DEBUG_PROPERTY_VALUE_ON
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import kotlin.reflect.KClass import kotlin.reflect.KClass
object Core { object Core {
const val TAG = "Core"
lateinit var app: Application lateinit var app: Application
@VisibleForTesting set @VisibleForTesting set
lateinit var configureIntent: (Context) -> PendingIntent lateinit var configureIntent: (Context) -> PendingIntent
...@@ -66,7 +69,6 @@ object Core { ...@@ -66,7 +69,6 @@ object Core {
val notification by lazy { app.getSystemService<NotificationManager>()!! } val notification by lazy { app.getSystemService<NotificationManager>()!! }
val packageInfo: PackageInfo by lazy { getPackageInfo(app.packageName) } val packageInfo: PackageInfo by lazy { getPackageInfo(app.packageName) }
val deviceStorage by lazy { if (Build.VERSION.SDK_INT < 24) app else DeviceStorageApp(app) } val deviceStorage by lazy { if (Build.VERSION.SDK_INT < 24) app else DeviceStorageApp(app) }
val analytics: FirebaseAnalytics by lazy { FirebaseAnalytics.getInstance(deviceStorage) }
val directBootSupported by lazy { val directBootSupported by lazy {
Build.VERSION.SDK_INT >= 24 && app.getSystemService<DevicePolicyManager>()?.storageEncryptionStatus == Build.VERSION.SDK_INT >= 24 && app.getSystemService<DevicePolicyManager>()?.storageEncryptionStatus ==
DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER
...@@ -104,8 +106,18 @@ object Core { ...@@ -104,8 +106,18 @@ object Core {
// overhead of debug mode is minimal: https://github.com/Kotlin/kotlinx.coroutines/blob/f528898/docs/debugging.md#debug-mode // overhead of debug mode is minimal: https://github.com/Kotlin/kotlinx.coroutines/blob/f528898/docs/debugging.md#debug-mode
System.setProperty(DEBUG_PROPERTY_NAME, DEBUG_PROPERTY_VALUE_ON) System.setProperty(DEBUG_PROPERTY_NAME, DEBUG_PROPERTY_VALUE_ON)
Fabric.with(deviceStorage, Crashlytics()) // multiple processes needs manual set-up Firebase.initialize(deviceStorage) // multiple processes needs manual set-up
FirebaseApp.initializeApp(deviceStorage) Timber.plant(object : Timber.DebugTree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
if (t == null) {
if (priority != Log.DEBUG || BuildConfig.DEBUG) Log.println(priority, tag, message)
FirebaseCrashlytics.getInstance().log("${"XXVDIWEF".getOrElse(priority) { 'X' }}/$tag: $message")
} else {
if (priority >= Log.WARN || priority == Log.DEBUG) Log.println(priority, tag, message)
if (priority >= Log.INFO) FirebaseCrashlytics.getInstance().recordException(t)
}
}
})
WorkManager.initialize(deviceStorage, Configuration.Builder().apply { WorkManager.initialize(deviceStorage, Configuration.Builder().apply {
setExecutor { GlobalScope.launch { it.run() } } setExecutor { GlobalScope.launch { it.run() } }
setTaskExecutor { GlobalScope.launch { it.run() } } setTaskExecutor { GlobalScope.launch { it.run() } }
...@@ -121,7 +133,7 @@ object Core { ...@@ -121,7 +133,7 @@ object Core {
File(deviceStorage.noBackupFilesDir, file).outputStream().use { output -> input.copyTo(output) } File(deviceStorage.noBackupFilesDir, file).outputStream().use { output -> input.copyTo(output) }
} }
} catch (e: IOException) { } catch (e: IOException) {
printLog(e) Timber.w(e)
} }
DataStore.publicStore.putLong(Key.assetUpdateTime, packageInfo.lastUpdateTime) DataStore.publicStore.putLong(Key.assetUpdateTime, packageInfo.lastUpdateTime)
} }
......
...@@ -26,19 +26,17 @@ import android.content.Intent ...@@ -26,19 +26,17 @@ import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.net.VpnService import android.net.VpnService
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.core.R import com.github.shadowsocks.core.R
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.broadcastReceiver import com.github.shadowsocks.utils.broadcastReceiver
import timber.log.Timber
class VpnRequestActivity : AppCompatActivity() { class VpnRequestActivity : AppCompatActivity() {
companion object { companion object {
private const val TAG = "VpnRequestActivity"
private const val REQUEST_CONNECT = 1 private const val REQUEST_CONNECT = 1
} }
...@@ -66,7 +64,7 @@ class VpnRequestActivity : AppCompatActivity() { ...@@ -66,7 +64,7 @@ class VpnRequestActivity : AppCompatActivity() {
if (requestCode != REQUEST_CONNECT) return super.onActivityResult(requestCode, resultCode, data) if (requestCode != REQUEST_CONNECT) return super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK) Core.startService() else { if (resultCode == RESULT_OK) Core.startService() else {
Toast.makeText(this, R.string.vpn_permission_denied, Toast.LENGTH_LONG).show() Toast.makeText(this, R.string.vpn_permission_denied, Toast.LENGTH_LONG).show()
Crashlytics.log(Log.ERROR, TAG, "Failed to start VpnService from onActivityResult: $data") Timber.e("Failed to start VpnService from onActivityResult: $data")
} }
finish() finish()
} }
......
...@@ -21,9 +21,7 @@ ...@@ -21,9 +21,7 @@
package com.github.shadowsocks.acl package com.github.shadowsocks.acl
import android.content.Context import android.content.Context
import android.util.Log
import androidx.recyclerview.widget.SortedList import androidx.recyclerview.widget.SortedList
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.net.Subnet import com.github.shadowsocks.net.Subnet
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
...@@ -33,6 +31,7 @@ import com.github.shadowsocks.utils.asIterable ...@@ -33,6 +31,7 @@ import com.github.shadowsocks.utils.asIterable
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.ensureActive import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import timber.log.Timber
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.io.Reader import java.io.Reader
...@@ -42,7 +41,6 @@ import kotlin.coroutines.coroutineContext ...@@ -42,7 +41,6 @@ import kotlin.coroutines.coroutineContext
class Acl { class Acl {
companion object { companion object {
const val TAG = "Acl"
const val ALL = "all" const val ALL = "all"
const val BYPASS_LAN = "bypass-lan" const val BYPASS_LAN = "bypass-lan"
const val BYPASS_CHN = "bypass-china" const val BYPASS_CHN = "bypass-china"
...@@ -159,7 +157,7 @@ class Acl { ...@@ -159,7 +157,7 @@ class Acl {
val child = Acl().fromReader(connect(url).getInputStream().bufferedReader(), bypass) val child = Acl().fromReader(connect(url).getInputStream().bufferedReader(), bypass)
child.flatten(depth - 1, connect) child.flatten(depth - 1, connect)
if (bypass != child.bypass) { if (bypass != child.bypass) {
Crashlytics.log(Log.WARN, TAG, "Imported network ACL has a conflicting mode set. " + Timber.w("Imported network ACL has a conflicting mode set. " +
"This will probably not work as intended. URL: $url") "This will probably not work as intended. URL: $url")
child.subnets.clear() // subnets for the different mode are discarded child.subnets.clear() // subnets for the different mode are discarded
child.bypass = bypass child.bypass = bypass
......
...@@ -24,6 +24,7 @@ import android.content.Context ...@@ -24,6 +24,7 @@ import android.content.Context
import androidx.work.* import androidx.work.*
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.utils.useCancellable import com.github.shadowsocks.utils.useCancellable
import timber.log.Timber
import java.io.IOException import java.io.IOException
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
...@@ -52,7 +53,7 @@ class AclSyncer(context: Context, workerParams: WorkerParameters) : CoroutineWor ...@@ -52,7 +53,7 @@ class AclSyncer(context: Context, workerParams: WorkerParameters) : CoroutineWor
Acl.getFile(route).printWriter().use { it.write(acl) } Acl.getFile(route).printWriter().use { it.write(acl) }
Result.success() Result.success()
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() Timber.d(e)
if (runAttemptCount > 5) Result.failure() else Result.retry() if (runAttemptCount > 5) Result.failure() else Result.retry()
} }
} }
...@@ -25,10 +25,7 @@ import android.content.Context ...@@ -25,10 +25,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.os.* import android.os.*
import android.util.Log
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.os.bundleOf
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.BootReceiver import com.github.shadowsocks.BootReceiver
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.Core.app import com.github.shadowsocks.Core.app
...@@ -40,9 +37,16 @@ import com.github.shadowsocks.core.R ...@@ -40,9 +37,16 @@ import com.github.shadowsocks.core.R
import com.github.shadowsocks.net.DnsResolverCompat import com.github.shadowsocks.net.DnsResolverCompat
import com.github.shadowsocks.net.HostsFile import com.github.shadowsocks.net.HostsFile
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.* import com.github.shadowsocks.utils.Action
import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.broadcastReceiver
import com.github.shadowsocks.utils.readableMessage
import com.google.firebase.analytics.FirebaseAnalytics import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.analytics.ktx.logEvent
import com.google.firebase.ktx.Firebase
import kotlinx.coroutines.* import kotlinx.coroutines.*
import timber.log.Timber
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
...@@ -122,7 +126,7 @@ object BaseService { ...@@ -122,7 +126,7 @@ object BaseService {
work(callbacks.getBroadcastItem(it)) work(callbacks.getBroadcastItem(it))
} catch (_: RemoteException) { } catch (_: RemoteException) {
} catch (e: Exception) { } catch (e: Exception) {
printLog(e) Timber.w(e)
} }
} }
} finally { } finally {
...@@ -229,7 +233,7 @@ object BaseService { ...@@ -229,7 +233,7 @@ object BaseService {
when { when {
s == State.Stopped -> startRunner() s == State.Stopped -> startRunner()
s.canStop -> stopRunner(true) s.canStop -> stopRunner(true)
else -> Crashlytics.log(Log.WARN, tag, "Illegal state when invoking use: $s") else -> Timber.w("Illegal state $s when invoking use")
} }
} }
...@@ -271,7 +275,7 @@ object BaseService { ...@@ -271,7 +275,7 @@ object BaseService {
// channge the state // channge the state
data.changeState(State.Stopping) data.changeState(State.Stopping)
GlobalScope.launch(Dispatchers.Main.immediate) { GlobalScope.launch(Dispatchers.Main.immediate) {
Core.analytics.logEvent("stop", bundleOf(Pair(FirebaseAnalytics.Param.METHOD, tag))) Firebase.analytics.logEvent("stop") { param(FirebaseAnalytics.Param.METHOD, tag) }
data.connectingJob?.cancelAndJoin() // ensure stop connecting first data.connectingJob?.cancelAndJoin() // ensure stop connecting first
this@Interface as Service this@Interface as Service
// we use a coroutineScope here to allow clean-up in parallel // we use a coroutineScope here to allow clean-up in parallel
...@@ -344,7 +348,7 @@ object BaseService { ...@@ -344,7 +348,7 @@ object BaseService {
} }
data.notification = createNotification(profile.formattedName) data.notification = createNotification(profile.formattedName)
Core.analytics.logEvent("start", bundleOf(Pair(FirebaseAnalytics.Param.METHOD, tag))) Firebase.analytics.logEvent("start") { param(FirebaseAnalytics.Param.METHOD, tag) }
data.changeState(State.Connecting) data.changeState(State.Connecting)
data.connectingJob = GlobalScope.launch(Dispatchers.Main) { data.connectingJob = GlobalScope.launch(Dispatchers.Main) {
...@@ -365,7 +369,7 @@ object BaseService { ...@@ -365,7 +369,7 @@ object BaseService {
} }
data.processes = GuardedProcessPool { data.processes = GuardedProcessPool {
printLog(it) Timber.w(it)
stopRunner(false, it.readableMessage) stopRunner(false, it.readableMessage)
} }
startProcesses(hosts) startProcesses(hosts)
...@@ -379,7 +383,7 @@ object BaseService { ...@@ -379,7 +383,7 @@ object BaseService {
} catch (_: UnknownHostException) { } catch (_: UnknownHostException) {
stopRunner(false, getString(R.string.invalid_server)) stopRunner(false, getString(R.string.invalid_server))
} catch (exc: Throwable) { } catch (exc: Throwable) {
if (exc is ExpectedException) exc.printStackTrace() else printLog(exc) if (exc is ExpectedException) Timber.d(exc) else Timber.w(exc)
stopRunner(false, "${getString(R.string.service_failed)}: ${exc.readableMessage}") stopRunner(false, "${getString(R.string.service_failed)}: ${exc.readableMessage}")
} finally { } finally {
data.connectingJob = null data.connectingJob = null
......
...@@ -24,8 +24,7 @@ import android.system.ErrnoException ...@@ -24,8 +24,7 @@ import android.system.ErrnoException
import android.system.Os import android.system.Os
import android.system.OsConstants import android.system.OsConstants
import android.text.TextUtils import android.text.TextUtils
import android.util.Log import timber.log.Timber
import com.crashlytics.android.Crashlytics
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
...@@ -47,9 +46,8 @@ object Executable { ...@@ -47,9 +46,8 @@ object Executable {
Os.kill(process.name.toInt(), OsConstants.SIGKILL) Os.kill(process.name.toInt(), OsConstants.SIGKILL)
} catch (e: ErrnoException) { } catch (e: ErrnoException) {
if (e.errno != OsConstants.ESRCH) { if (e.errno != OsConstants.ESRCH) {
e.printStackTrace() Timber.w("SIGKILL ${exe.absolutePath} (${process.name}) failed")
Crashlytics.log(Log.WARN, "kill", "SIGKILL ${exe.absolutePath} (${process.name}) failed") Timber.w(e)
Crashlytics.logException(e)
} }
} }
} }
......
...@@ -25,13 +25,12 @@ import android.os.SystemClock ...@@ -25,13 +25,12 @@ import android.os.SystemClock
import android.system.ErrnoException import android.system.ErrnoException
import android.system.Os import android.system.Os
import android.system.OsConstants import android.system.OsConstants
import android.util.Log
import androidx.annotation.MainThread import androidx.annotation.MainThread
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.utils.Commandline import com.github.shadowsocks.utils.Commandline
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import timber.log.Timber
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
...@@ -39,7 +38,6 @@ import kotlin.concurrent.thread ...@@ -39,7 +38,6 @@ import kotlin.concurrent.thread
class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : CoroutineScope { class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : CoroutineScope {
companion object { companion object {
private const val TAG = "GuardedProcessPool"
private val pid by lazy { private val pid by lazy {
Class.forName("java.lang.ProcessManager\$ProcessImpl").getDeclaredField("pid").apply { isAccessible = true } Class.forName("java.lang.ProcessManager\$ProcessImpl").getDeclaredField("pid").apply { isAccessible = true }
} }
...@@ -63,10 +61,10 @@ class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : C ...@@ -63,10 +61,10 @@ class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : C
try { try {
while (true) { while (true) {
thread(name = "stderr-$cmdName") { thread(name = "stderr-$cmdName") {
streamLogger(process.errorStream) { Crashlytics.log(Log.ERROR, cmdName, it) } streamLogger(process.errorStream) { Timber.tag(cmdName).e(it) }
} }
thread(name = "stdout-$cmdName") { thread(name = "stdout-$cmdName") {
streamLogger(process.inputStream) { Crashlytics.log(Log.VERBOSE, cmdName, it) } streamLogger(process.inputStream) { Timber.tag(cmdName).v(it) }
// this thread also acts as a daemon thread for waitFor // this thread also acts as a daemon thread for waitFor
runBlocking { exitChannel.send(process.waitFor()) } runBlocking { exitChannel.send(process.waitFor()) }
} }
...@@ -76,17 +74,16 @@ class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : C ...@@ -76,17 +74,16 @@ class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : C
when { when {
SystemClock.elapsedRealtime() - startTime < 1000 -> throw IOException( SystemClock.elapsedRealtime() - startTime < 1000 -> throw IOException(
"$cmdName exits too fast (exit code: $exitCode)") "$cmdName exits too fast (exit code: $exitCode)")
exitCode == 128 + OsConstants.SIGKILL -> Crashlytics.log(Log.WARN, TAG, "$cmdName was killed") exitCode == 128 + OsConstants.SIGKILL -> Timber.w("$cmdName was killed")
else -> Crashlytics.logException(IOException("$cmdName unexpectedly exits with code $exitCode")) else -> Timber.w(IOException("$cmdName unexpectedly exits with code $exitCode"))
} }
Crashlytics.log(Log.DEBUG, TAG, Timber.i("restart process: ${Commandline.toString(cmd)} (last exit code: $exitCode)")
"restart process: ${Commandline.toString(cmd)} (last exit code: $exitCode)")
start() start()
running = true running = true
onRestartCallback?.invoke() onRestartCallback?.invoke()
} }
} catch (e: IOException) { } catch (e: IOException) {
Crashlytics.log(Log.WARN, TAG, "error occurred. stop guard: " + Commandline.toString(cmd)) Timber.w("error occurred. stop guard: ${Commandline.toString(cmd)}")
GlobalScope.launch(Dispatchers.Main) { onFatal(e) } GlobalScope.launch(Dispatchers.Main) { onFatal(e) }
} finally { } finally {
if (running) withContext(NonCancellable) { // clean-up cannot be cancelled if (running) withContext(NonCancellable) { // clean-up cannot be cancelled
...@@ -94,9 +91,9 @@ class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : C ...@@ -94,9 +91,9 @@ class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : C
try { try {
Os.kill(pid.get(process) as Int, OsConstants.SIGTERM) Os.kill(pid.get(process) as Int, OsConstants.SIGTERM)
} catch (e: ErrnoException) { } catch (e: ErrnoException) {
if (e.errno != OsConstants.ESRCH) Crashlytics.logException(e) if (e.errno != OsConstants.ESRCH) Timber.w(e)
} catch (e: ReflectiveOperationException) { } catch (e: ReflectiveOperationException) {
Crashlytics.logException(e) Timber.w(e)
} }
if (withTimeoutOrNull(500) { exitChannel.receive() } != null) return@withContext if (withTimeoutOrNull(500) { exitChannel.receive() } != null) return@withContext
} }
...@@ -115,7 +112,7 @@ class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : C ...@@ -115,7 +112,7 @@ class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : C
@MainThread @MainThread
fun start(cmd: List<String>, onRestartCallback: (suspend () -> Unit)? = null) { fun start(cmd: List<String>, onRestartCallback: (suspend () -> Unit)? = null) {
Crashlytics.log(Log.DEBUG, TAG, "start process: " + Commandline.toString(cmd)) Timber.i("start process: ${Commandline.toString(cmd)}")
Guard(cmd).apply { Guard(cmd).apply {
start() // if start fails, IOException will be thrown directly start() // if start fails, IOException will be thrown directly
launch { looper(onRestartCallback) } launch { looper(onRestartCallback) }
......
package com.github.shadowsocks.bg package com.github.shadowsocks.bg
import android.net.LocalSocket import android.net.LocalSocket
import android.util.Log
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.net.ConcurrentLocalSocketListener import com.github.shadowsocks.net.ConcurrentLocalSocketListener
import com.github.shadowsocks.net.DnsResolverCompat import com.github.shadowsocks.net.DnsResolverCompat
import com.github.shadowsocks.utils.printLog
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.xbill.DNS.Message import org.xbill.DNS.Message
import org.xbill.DNS.Rcode import org.xbill.DNS.Rcode
import timber.log.Timber
import java.io.DataInputStream import java.io.DataInputStream
import java.io.DataOutputStream import java.io.DataOutputStream
import java.io.File import java.io.File
...@@ -31,10 +29,10 @@ class LocalDnsWorker(private val resolver: suspend (ByteArray) -> ByteArray) : C ...@@ -31,10 +29,10 @@ class LocalDnsWorker(private val resolver: suspend (ByteArray) -> ByteArray) : C
resolver(query) resolver(query)
} catch (e: Exception) { } catch (e: Exception) {
when (e) { when (e) {
is TimeoutCancellationException -> Crashlytics.log(Log.WARN, name, "Resolving timed out") is TimeoutCancellationException -> Timber.w("Resolving timed out")
is CancellationException -> { } // ignore is CancellationException -> { } // ignore
is IOException -> Crashlytics.log(Log.WARN, name, e.message) is IOException -> Timber.d(e)
else -> printLog(e) else -> Timber.w(e)
} }
try { try {
DnsResolverCompat.prepareDnsResponse(Message(query)).apply { DnsResolverCompat.prepareDnsResponse(Message(query)).apply {
......
...@@ -34,6 +34,7 @@ import com.github.shadowsocks.preference.DataStore ...@@ -34,6 +34,7 @@ import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.parseNumericAddress import com.github.shadowsocks.utils.parseNumericAddress
import com.github.shadowsocks.utils.signaturesCompat import com.github.shadowsocks.utils.signaturesCompat
import com.github.shadowsocks.utils.useCancellable import com.github.shadowsocks.utils.useCancellable
import com.google.firebase.remoteconfig.ktx.get
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
...@@ -60,7 +61,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro ...@@ -60,7 +61,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
scheduleConfigUpdate = !success scheduleConfigUpdate = !success
val conn = withContext(Dispatchers.IO) { val conn = withContext(Dispatchers.IO) {
// Network.openConnection might use networking, see https://issuetracker.google.com/issues/135242093 // Network.openConnection might use networking, see https://issuetracker.google.com/issues/135242093
service.openConnection(URL(config.getString("proxy_url"))) as HttpURLConnection service.openConnection(URL(config["proxy_url"].asString())) as HttpURLConnection
} }
conn.requestMethod = "POST" conn.requestMethod = "POST"
conn.doOutput = true conn.doOutput = true
......
...@@ -20,19 +20,20 @@ ...@@ -20,19 +20,20 @@
package com.github.shadowsocks.bg package com.github.shadowsocks.bg
import android.util.Log
import androidx.core.os.bundleOf
import com.github.shadowsocks.Core
import com.github.shadowsocks.core.R import com.github.shadowsocks.core.R
import com.google.firebase.remoteconfig.FirebaseRemoteConfig import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.analytics.ktx.logEvent
import com.google.firebase.ktx.Firebase
import com.google.firebase.remoteconfig.ktx.remoteConfig
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.tasks.await import kotlinx.coroutines.tasks.await
import timber.log.Timber
object RemoteConfig { object RemoteConfig {
private val config = GlobalScope.async(Dispatchers.Main.immediate) { private val config = GlobalScope.async(Dispatchers.Main.immediate) {
FirebaseRemoteConfig.getInstance().apply { setDefaultsAsync(R.xml.default_configs).await() } Firebase.remoteConfig.apply { setDefaultsAsync(R.xml.default_configs).await() }
} }
fun fetchAsync() = GlobalScope.async(Dispatchers.Main.immediate) { fetch() } fun fetchAsync() = GlobalScope.async(Dispatchers.Main.immediate) { fetch() }
...@@ -42,8 +43,8 @@ object RemoteConfig { ...@@ -42,8 +43,8 @@ object RemoteConfig {
fetch().await() fetch().await()
this to true this to true
} catch (e: Exception) { } catch (e: Exception) {
Log.w("RemoteConfig", e) Timber.d(e)
Core.analytics.logEvent("femote_config_failure", bundleOf(Pair(javaClass.simpleName, e.message))) Firebase.analytics.logEvent("femote_config_failure") { param("message", e.toString()) }
this to false this to false
} }
} }
......
...@@ -39,9 +39,13 @@ import com.github.shadowsocks.preference.DataStore ...@@ -39,9 +39,13 @@ import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.closeQuietly import com.github.shadowsocks.utils.closeQuietly
import com.github.shadowsocks.utils.int import com.github.shadowsocks.utils.int
import com.github.shadowsocks.utils.printLog import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.* import kotlinx.coroutines.delay
import java.io.* import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.File
import java.io.FileDescriptor
import java.io.IOException
import java.net.URL import java.net.URL
import android.net.VpnService as BaseVpnService import android.net.VpnService as BaseVpnService
...@@ -67,13 +71,13 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -67,13 +71,13 @@ class VpnService : BaseVpnService(), BaseService.Interface {
} catch (e: IOException) { } catch (e: IOException) {
when ((e.cause as? ErrnoException)?.errno) { when ((e.cause as? ErrnoException)?.errno) {
// also suppress ENONET (Machine is not on the network) // also suppress ENONET (Machine is not on the network)
OsConstants.EPERM, 64 -> e.printStackTrace() OsConstants.EPERM, 64 -> Timber.d(e)
else -> printLog(e) else -> Timber.w(e)
} }
return@let false return@let false
} catch (e: ReflectiveOperationException) { } catch (e: ReflectiveOperationException) {
check(Build.VERSION.SDK_INT < 23) check(Build.VERSION.SDK_INT < 23)
printLog(e) Timber.w(e)
} }
protect(fd.int) protect(fd.int)
}) 0 else 1) }) 0 else 1)
...@@ -169,7 +173,7 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -169,7 +173,7 @@ class VpnService : BaseVpnService(), BaseService.Interface {
if (profile.bypass) builder.addDisallowedApplication(it) if (profile.bypass) builder.addDisallowedApplication(it)
else builder.addAllowedApplication(it) else builder.addAllowedApplication(it)
} catch (ex: PackageManager.NameNotFoundException) { } catch (ex: PackageManager.NameNotFoundException) {
printLog(ex) Timber.w(ex)
} }
} }
if (!profile.bypass) builder.addAllowedApplication(me) if (!profile.bypass) builder.addAllowedApplication(me)
......
...@@ -24,7 +24,6 @@ import android.annotation.TargetApi ...@@ -24,7 +24,6 @@ import android.annotation.TargetApi
import android.net.Uri import android.net.Uri
import android.os.Parcelable import android.os.Parcelable
import android.util.Base64 import android.util.Base64
import android.util.Log
import android.util.LongSparseArray import android.util.LongSparseArray
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.room.* import androidx.room.*
...@@ -40,6 +39,7 @@ import com.google.gson.JsonPrimitive ...@@ -40,6 +39,7 @@ import com.google.gson.JsonPrimitive
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
import timber.log.Timber
import java.io.Serializable import java.io.Serializable
import java.net.URI import java.net.URI
import java.net.URISyntaxException import java.net.URISyntaxException
...@@ -100,7 +100,6 @@ data class Profile( ...@@ -100,7 +100,6 @@ data class Profile(
} }
companion object { companion object {
private const val TAG = "ShadowParser"
private const val serialVersionUID = 1L private const val serialVersionUID = 1L
private const val sponsored = "198.199.101.152" private const val sponsored = "198.199.101.152"
private val pattern = private val pattern =
...@@ -124,7 +123,7 @@ data class Profile( ...@@ -124,7 +123,7 @@ data class Profile(
profile.name = uri.fragment profile.name = uri.fragment
profile profile
} else { } else {
Log.e(TAG, "Unrecognized URI: ${it.value}") Timber.e("Unrecognized URI: ${it.value}")
null null
} }
} else { } else {
...@@ -147,16 +146,16 @@ data class Profile( ...@@ -147,16 +146,16 @@ data class Profile(
profile.name = uri.fragment ?: "" profile.name = uri.fragment ?: ""
profile profile
} catch (e: URISyntaxException) { } catch (e: URISyntaxException) {
Log.e(TAG, "Invalid URI: ${it.value}") Timber.e("Invalid URI: ${it.value}")
null null
} }
} else { } else {
Log.e(TAG, "Unknown user info: ${it.value}") Timber.e("Unknown user info: ${it.value}")
null null
} }
} }
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
Log.e(TAG, "Invalid base64 detected: ${it.value}") Timber.e("Invalid base64 detected: ${it.value}")
null null
} }
}.filterNotNull() }.filterNotNull()
......
...@@ -26,9 +26,9 @@ import com.github.shadowsocks.Core ...@@ -26,9 +26,9 @@ import com.github.shadowsocks.Core
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.DirectBoot import com.github.shadowsocks.utils.DirectBoot
import com.github.shadowsocks.utils.forEachTry import com.github.shadowsocks.utils.forEachTry
import com.github.shadowsocks.utils.printLog
import com.google.gson.JsonStreamParser import com.google.gson.JsonStreamParser
import org.json.JSONArray import org.json.JSONArray
import timber.log.Timber
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.sql.SQLException import java.sql.SQLException
...@@ -94,7 +94,7 @@ object ProfileManager { ...@@ -94,7 +94,7 @@ object ProfileManager {
} catch (ex: SQLiteCantOpenDatabaseException) { } catch (ex: SQLiteCantOpenDatabaseException) {
throw IOException(ex) throw IOException(ex)
} catch (ex: SQLException) { } catch (ex: SQLException) {
printLog(ex) Timber.w(ex)
null null
} }
...@@ -122,7 +122,7 @@ object ProfileManager { ...@@ -122,7 +122,7 @@ object ProfileManager {
} catch (ex: SQLiteCantOpenDatabaseException) { } catch (ex: SQLiteCantOpenDatabaseException) {
throw IOException(ex) throw IOException(ex)
} catch (ex: SQLException) { } catch (ex: SQLException) {
printLog(ex) Timber.w(ex)
false false
} }
if (!nonEmpty) DataStore.profileId = createProfile().id if (!nonEmpty) DataStore.profileId = createProfile().id
...@@ -134,7 +134,7 @@ object ProfileManager { ...@@ -134,7 +134,7 @@ object ProfileManager {
} catch (ex: SQLiteCantOpenDatabaseException) { } catch (ex: SQLiteCantOpenDatabaseException) {
throw IOException(ex) throw IOException(ex)
} catch (ex: SQLException) { } catch (ex: SQLException) {
printLog(ex) Timber.w(ex)
null null
} }
...@@ -144,7 +144,7 @@ object ProfileManager { ...@@ -144,7 +144,7 @@ object ProfileManager {
} catch (ex: SQLiteCantOpenDatabaseException) { } catch (ex: SQLiteCantOpenDatabaseException) {
throw IOException(ex) throw IOException(ex)
} catch (ex: SQLException) { } catch (ex: SQLException) {
printLog(ex) Timber.w(ex)
null null
} }
} }
...@@ -21,13 +21,13 @@ ...@@ -21,13 +21,13 @@
package com.github.shadowsocks.net package com.github.shadowsocks.net
import android.net.LocalSocket import android.net.LocalSocket
import com.github.shadowsocks.utils.printLog
import kotlinx.coroutines.* import kotlinx.coroutines.*
import timber.log.Timber
import java.io.File import java.io.File
abstract class ConcurrentLocalSocketListener(name: String, socketFile: File) : LocalSocketListener(name, socketFile), abstract class ConcurrentLocalSocketListener(name: String, socketFile: File) : LocalSocketListener(name, socketFile),
CoroutineScope { CoroutineScope {
override val coroutineContext = Dispatchers.IO + SupervisorJob() + CoroutineExceptionHandler { _, t -> printLog(t) } override val coroutineContext = Dispatchers.IO + SupervisorJob() + CoroutineExceptionHandler { _, t -> Timber.w(t) }
override fun accept(socket: LocalSocket) { override fun accept(socket: LocalSocket) {
launch { super.accept(socket) } launch { super.accept(socket) }
......
...@@ -27,12 +27,12 @@ import android.net.NetworkCapabilities ...@@ -27,12 +27,12 @@ import android.net.NetworkCapabilities
import android.net.NetworkRequest import android.net.NetworkRequest
import android.os.Build import android.os.Build
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.utils.printLog
import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.actor import kotlinx.coroutines.channels.actor
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import timber.log.Timber
import java.net.UnknownHostException import java.net.UnknownHostException
object DefaultNetworkListener { object DefaultNetworkListener {
...@@ -124,7 +124,7 @@ object DefaultNetworkListener { ...@@ -124,7 +124,7 @@ object DefaultNetworkListener {
Core.connectivity.requestNetwork(request, Callback) Core.connectivity.requestNetwork(request, Callback)
} catch (e: SecurityException) { } catch (e: SecurityException) {
// known bug: https://stackoverflow.com/a/33509180/2245107 // known bug: https://stackoverflow.com/a/33509180/2245107
if (Build.VERSION.SDK_INT != 23) printLog(e) if (Build.VERSION.SDK_INT != 23) Timber.w(e)
fallback = true fallback = true
} }
} }
......
...@@ -34,9 +34,9 @@ import com.github.shadowsocks.Core ...@@ -34,9 +34,9 @@ import com.github.shadowsocks.Core
import com.github.shadowsocks.utils.closeQuietly import com.github.shadowsocks.utils.closeQuietly
import com.github.shadowsocks.utils.int import com.github.shadowsocks.utils.int
import com.github.shadowsocks.utils.parseNumericAddress import com.github.shadowsocks.utils.parseNumericAddress
import com.github.shadowsocks.utils.printLog
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.xbill.DNS.* import org.xbill.DNS.*
import timber.log.Timber
import java.io.FileDescriptor import java.io.FileDescriptor
import java.io.IOException import java.io.IOException
import java.net.Inet4Address import java.net.Inet4Address
...@@ -80,7 +80,7 @@ sealed class DnsResolverCompat { ...@@ -80,7 +80,7 @@ sealed class DnsResolverCompat {
false false
} catch (e: ReflectiveOperationException) { } catch (e: ReflectiveOperationException) {
check(Build.VERSION.SDK_INT < 23) check(Build.VERSION.SDK_INT < 23)
printLog(e) Timber.w(e)
checkConnectivity(network, addr) checkConnectivity(network, addr)
} }
private fun checkConnectivity(network: Network, addr: InetAddress): Boolean { private fun checkConnectivity(network: Network, addr: InetAddress): Boolean {
...@@ -88,7 +88,7 @@ sealed class DnsResolverCompat { ...@@ -88,7 +88,7 @@ sealed class DnsResolverCompat {
try { try {
it.matches(addr) it.matches(addr)
} catch (e: RuntimeException) { } catch (e: RuntimeException) {
printLog(e) Timber.w(e)
false false
} }
} == true } == true
......
...@@ -26,11 +26,11 @@ import android.net.LocalSocketAddress ...@@ -26,11 +26,11 @@ import android.net.LocalSocketAddress
import android.system.ErrnoException import android.system.ErrnoException
import android.system.Os import android.system.Os
import android.system.OsConstants import android.system.OsConstants
import com.github.shadowsocks.utils.printLog
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.sendBlocking import kotlinx.coroutines.channels.sendBlocking
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
...@@ -55,7 +55,7 @@ abstract class LocalSocketListener(name: String, socketFile: File) : Thread(name ...@@ -55,7 +55,7 @@ abstract class LocalSocketListener(name: String, socketFile: File) : Thread(name
try { try {
accept(serverSocket.accept()) accept(serverSocket.accept())
} catch (e: IOException) { } catch (e: IOException) {
if (running) printLog(e) if (running) Timber.w(e)
continue continue
} }
} }
......
...@@ -20,9 +20,8 @@ ...@@ -20,9 +20,8 @@
package com.github.shadowsocks.plugin package com.github.shadowsocks.plugin
import android.util.Log
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.utils.Commandline import com.github.shadowsocks.utils.Commandline
import timber.log.Timber
import java.util.* import java.util.*
class PluginConfiguration(val pluginsOptions: Map<String, PluginOptions>, val selected: String) { class PluginConfiguration(val pluginsOptions: Map<String, PluginOptions>, val selected: String) {
...@@ -44,7 +43,7 @@ class PluginConfiguration(val pluginsOptions: Map<String, PluginOptions>, val se ...@@ -44,7 +43,7 @@ class PluginConfiguration(val pluginsOptions: Map<String, PluginOptions>, val se
} }
} }
} catch (exc: Exception) { } catch (exc: Exception) {
Crashlytics.log(Log.WARN, "PluginConfiguration", exc.message) Timber.w(exc)
} }
opt opt
} else PluginOptions(line) } else PluginOptions(line)
......
...@@ -30,23 +30,20 @@ import android.content.pm.ProviderInfo ...@@ -30,23 +30,20 @@ import android.content.pm.ProviderInfo
import android.content.pm.Signature import android.content.pm.Signature
import android.database.Cursor import android.database.Cursor
import android.net.Uri import android.net.Uri
import android.os.Build
import android.system.Os import android.system.Os
import android.util.Base64 import android.util.Base64
import android.util.Log
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.Core.app import com.github.shadowsocks.Core.app
import com.github.shadowsocks.bg.BaseService import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.utils.listenForPackageChanges import com.github.shadowsocks.utils.listenForPackageChanges
import com.github.shadowsocks.utils.printLog
import com.github.shadowsocks.utils.signaturesCompat import com.github.shadowsocks.utils.signaturesCompat
import timber.log.Timber
import java.io.File import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
object PluginManager { object PluginManager {
private const val TAG = "PluginManager"
class PluginNotFoundException(private val plugin: String) : FileNotFoundException(plugin), class PluginNotFoundException(private val plugin: String) : FileNotFoundException(plugin),
BaseService.ExpectedException { BaseService.ExpectedException {
override fun getLocalizedMessage() = app.getString(com.github.shadowsocks.core.R.string.plugin_unknown, plugin) override fun getLocalizedMessage() = app.getString(com.github.shadowsocks.core.R.string.plugin_unknown, plugin)
...@@ -130,7 +127,7 @@ object PluginManager { ...@@ -130,7 +127,7 @@ object PluginManager {
val result = initNative(configuration) val result = initNative(configuration)
if (result != null) return result if (result != null) return result
} catch (t: Throwable) { } catch (t: Throwable) {
if (throwable == null) throwable = t else printLog(t) if (throwable == null) throwable = t else Timber.w(t)
} }
// add other plugin types here // add other plugin types here
...@@ -139,11 +136,12 @@ object PluginManager { ...@@ -139,11 +136,12 @@ object PluginManager {
} }
private fun initNative(configuration: PluginConfiguration): Pair<String, PluginOptions>? { private fun initNative(configuration: PluginConfiguration): Pair<String, PluginOptions>? {
var flags = PackageManager.GET_META_DATA
if (Build.VERSION.SDK_INT >= 24) {
flags = flags or PackageManager.MATCH_DIRECT_BOOT_UNAWARE or PackageManager.MATCH_DIRECT_BOOT_AWARE
}
val providers = app.packageManager.queryIntentContentProviders( val providers = app.packageManager.queryIntentContentProviders(
Intent(PluginContract.ACTION_NATIVE_PLUGIN, buildUri(configuration.selected)), Intent(PluginContract.ACTION_NATIVE_PLUGIN, buildUri(configuration.selected)), flags)
PackageManager.GET_META_DATA or
PackageManager.MATCH_DIRECT_BOOT_UNAWARE or PackageManager.MATCH_DIRECT_BOOT_AWARE
)
if (providers.isEmpty()) return null if (providers.isEmpty()) return null
val provider = providers.single().providerInfo val provider = providers.single().providerInfo
val options = configuration.getOptions { provider.loadString(PluginContract.METADATA_KEY_DEFAULT_CONFIG) } val options = configuration.getOptions { provider.loadString(PluginContract.METADATA_KEY_DEFAULT_CONFIG) }
...@@ -151,7 +149,7 @@ object PluginManager { ...@@ -151,7 +149,7 @@ object PluginManager {
try { try {
initNativeFaster(provider)?.also { return it to options } initNativeFaster(provider)?.also { return it to options }
} catch (t: Throwable) { } catch (t: Throwable) {
Crashlytics.log(Log.WARN, TAG, "Initializing native plugin faster mode failed") Timber.w("Initializing native plugin faster mode failed")
failure = t failure = t
} }
...@@ -162,7 +160,7 @@ object PluginManager { ...@@ -162,7 +160,7 @@ object PluginManager {
try { try {
return initNativeFast(app.contentResolver, options, uri)?.let { it to options } return initNativeFast(app.contentResolver, options, uri)?.let { it to options }
} catch (t: Throwable) { } catch (t: Throwable) {
Crashlytics.log(Log.WARN, TAG, "Initializing native plugin fast mode failed") Timber.w("Initializing native plugin fast mode failed")
failure?.also { t.addSuppressed(it) } failure?.also { t.addSuppressed(it) }
failure = t failure = t
} }
......
...@@ -27,7 +27,6 @@ import android.app.Service ...@@ -27,7 +27,6 @@ import android.app.Service
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.os.IBinder import android.os.IBinder
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
...@@ -42,6 +41,7 @@ import com.github.shadowsocks.preference.DataStore ...@@ -42,6 +41,7 @@ import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.* import com.github.shadowsocks.utils.*
import com.google.gson.JsonStreamParser import com.google.gson.JsonStreamParser
import kotlinx.coroutines.* import kotlinx.coroutines.*
import timber.log.Timber
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
...@@ -59,7 +59,7 @@ class SubscriptionService : Service(), CoroutineScope { ...@@ -59,7 +59,7 @@ class SubscriptionService : Service(), CoroutineScope {
app.getText(R.string.service_subscription), NotificationManager.IMPORTANCE_LOW) app.getText(R.string.service_subscription), NotificationManager.IMPORTANCE_LOW)
} }
override val coroutineContext = SupervisorJob() + CoroutineExceptionHandler { _, t -> printLog(t) } override val coroutineContext = SupervisorJob() + CoroutineExceptionHandler { _, t -> Timber.w(t) }
private var worker: Job? = null private var worker: Job? = null
private val cancelReceiver = broadcastReceiver { _, _ -> worker?.cancel() } private val cancelReceiver = broadcastReceiver { _, _ -> worker?.cancel() }
private var counter = 0 private var counter = 0
...@@ -135,7 +135,7 @@ class SubscriptionService : Service(), CoroutineScope { ...@@ -135,7 +135,7 @@ class SubscriptionService : Service(), CoroutineScope {
} }
return tempFile return tempFile
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() Timber.d(e)
launch(Dispatchers.Main) { launch(Dispatchers.Main) {
Toast.makeText(this@SubscriptionService, e.readableMessage, Toast.LENGTH_LONG).show() Toast.makeText(this@SubscriptionService, e.readableMessage, Toast.LENGTH_LONG).show()
} }
...@@ -175,8 +175,8 @@ class SubscriptionService : Service(), CoroutineScope { ...@@ -175,8 +175,8 @@ class SubscriptionService : Service(), CoroutineScope {
subscriptions.compute(it.name to it.formattedAddress) { _, oldProfile -> subscriptions.compute(it.name to it.formattedAddress) { _, oldProfile ->
when (oldProfile?.subscription) { when (oldProfile?.subscription) {
Profile.SubscriptionStatus.Active -> { Profile.SubscriptionStatus.Active -> {
Log.w("SubscriptionService", "Duplicate profiles detected. Please use different profile " + Timber.w("Duplicate profiles detected. Please use different profile names and/or " +
"names and/or address:port for better subscription support.") "address:port for better subscription support.")
oldProfile oldProfile
} }
Profile.SubscriptionStatus.Obsolete -> { Profile.SubscriptionStatus.Obsolete -> {
...@@ -195,7 +195,7 @@ class SubscriptionService : Service(), CoroutineScope { ...@@ -195,7 +195,7 @@ class SubscriptionService : Service(), CoroutineScope {
}!! }!!
} }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() Timber.d(e)
Toast.makeText(this, e.readableMessage, Toast.LENGTH_LONG).show() Toast.makeText(this, e.readableMessage, Toast.LENGTH_LONG).show()
} }
......
...@@ -34,11 +34,11 @@ import android.system.OsConstants ...@@ -34,11 +34,11 @@ import android.system.OsConstants
import android.util.TypedValue import android.util.TypedValue
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.preference.Preference import androidx.preference.Preference
import com.crashlytics.android.Crashlytics
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import timber.log.Timber
import java.io.FileDescriptor import java.io.FileDescriptor
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.InetAddress import java.net.InetAddress
...@@ -53,7 +53,7 @@ fun <T> Iterable<T>.forEachTry(action: (T) -> Unit) { ...@@ -53,7 +53,7 @@ fun <T> Iterable<T>.forEachTry(action: (T) -> Unit) {
if (result == null) result = e else result.addSuppressed(e) if (result == null) result = e else result.addSuppressed(e)
} }
if (result != null) { if (result != null) {
result.printStackTrace() Timber.d(result)
throw result throw result
} }
} }
...@@ -140,9 +140,4 @@ fun Resources.Theme.resolveResourceId(@AttrRes resId: Int): Int { ...@@ -140,9 +140,4 @@ fun Resources.Theme.resolveResourceId(@AttrRes resId: Int): Int {
val Intent.datas get() = listOfNotNull(data) + (clipData?.asIterable()?.mapNotNull { it.uri } ?: emptyList()) val Intent.datas get() = listOfNotNull(data) + (clipData?.asIterable()?.mapNotNull { it.uri } ?: emptyList())
fun printLog(t: Throwable) {
Crashlytics.logException(t)
t.printStackTrace()
}
fun Preference.remove() = parent!!.removePreference(this) fun Preference.remove() = parent!!.removePreference(this)
...@@ -26,7 +26,7 @@ import android.util.AttributeSet ...@@ -26,7 +26,7 @@ import android.util.AttributeSet
import android.view.MotionEvent import android.view.MotionEvent
import androidx.appcompat.widget.AppCompatTextView import androidx.appcompat.widget.AppCompatTextView
import androidx.core.view.isGone import androidx.core.view.isGone
import com.github.shadowsocks.utils.printLog import timber.log.Timber
class AutoCollapseTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, class AutoCollapseTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null,
defStyleAttr: Int = 0) : defStyleAttr: Int = 0) :
...@@ -40,13 +40,13 @@ class AutoCollapseTextView @JvmOverloads constructor(context: Context, attrs: At ...@@ -40,13 +40,13 @@ class AutoCollapseTextView @JvmOverloads constructor(context: Context, attrs: At
override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) = try { override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) = try {
super.onFocusChanged(focused, direction, previouslyFocusedRect) super.onFocusChanged(focused, direction, previouslyFocusedRect)
} catch (e: IndexOutOfBoundsException) { } catch (e: IndexOutOfBoundsException) {
printLog(e) Timber.w(e)
} }
override fun onTouchEvent(event: MotionEvent?) = try { override fun onTouchEvent(event: MotionEvent?) = try {
super.onTouchEvent(event) super.onTouchEvent(event)
} catch (e: IndexOutOfBoundsException) { } catch (e: IndexOutOfBoundsException) {
printLog(e) Timber.w(e)
false false
} }
} }
...@@ -4,8 +4,8 @@ import java.util.regex.Pattern ...@@ -4,8 +4,8 @@ import java.util.regex.Pattern
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'com.google.android.gms.oss-licenses-plugin' apply plugin: 'com.google.android.gms.oss-licenses-plugin'
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
apply plugin: 'io.fabric'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
......
...@@ -28,7 +28,6 @@ import android.net.VpnService ...@@ -28,7 +28,6 @@ import android.net.VpnService
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.RemoteException import android.os.RemoteException
import android.util.Log
import android.view.* import android.view.*
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.browser.customtabs.CustomTabColorSchemeParams import androidx.browser.customtabs.CustomTabColorSchemeParams
...@@ -40,7 +39,6 @@ import androidx.core.view.GravityCompat ...@@ -40,7 +39,6 @@ import androidx.core.view.GravityCompat
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.preference.PreferenceDataStore import androidx.preference.PreferenceDataStore
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.acl.CustomRulesFragment import com.github.shadowsocks.acl.CustomRulesFragment
import com.github.shadowsocks.aidl.IShadowsocksService import com.github.shadowsocks.aidl.IShadowsocksService
import com.github.shadowsocks.aidl.ShadowsocksConnection import com.github.shadowsocks.aidl.ShadowsocksConnection
...@@ -56,11 +54,14 @@ import com.github.shadowsocks.widget.ServiceButton ...@@ -56,11 +54,14 @@ import com.github.shadowsocks.widget.ServiceButton
import com.github.shadowsocks.widget.StatsBar import com.github.shadowsocks.widget.StatsBar
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.analytics.ktx.logEvent
import com.google.firebase.ktx.Firebase
import timber.log.Timber
class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPreferenceDataStoreChangeListener, class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPreferenceDataStoreChangeListener,
NavigationView.OnNavigationItemSelectedListener { NavigationView.OnNavigationItemSelectedListener {
companion object { companion object {
private const val TAG = "ShadowsocksMainActivity"
private const val REQUEST_CONNECT = 1 private const val REQUEST_CONNECT = 1
var stateListener: ((BaseService.State) -> Unit)? = null var stateListener: ((BaseService.State) -> Unit)? = null
...@@ -148,7 +149,7 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref ...@@ -148,7 +149,7 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref
resultCode == Activity.RESULT_OK -> Core.startService() resultCode == Activity.RESULT_OK -> Core.startService()
else -> { else -> {
snackbar().setText(R.string.vpn_permission_denied).show() snackbar().setText(R.string.vpn_permission_denied).show()
Crashlytics.log(Log.ERROR, TAG, "Failed to start VpnService from onActivityResult: $data") Timber.e("Failed to start VpnService from onActivityResult: $data")
} }
} }
} }
...@@ -208,7 +209,7 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref ...@@ -208,7 +209,7 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref
} }
R.id.globalSettings -> displayFragment(GlobalSettingsFragment()) R.id.globalSettings -> displayFragment(GlobalSettingsFragment())
R.id.about -> { R.id.about -> {
Core.analytics.logEvent("about", Bundle()) Firebase.analytics.logEvent("about") { }
displayFragment(AboutFragment()) displayFragment(AboutFragment())
} }
R.id.faq -> { R.id.faq -> {
......
...@@ -44,7 +44,6 @@ import androidx.core.os.bundleOf ...@@ -44,7 +44,6 @@ import androidx.core.os.bundleOf
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.* import androidx.recyclerview.widget.*
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.aidl.TrafficStats import com.github.shadowsocks.aidl.TrafficStats
import com.github.shadowsocks.bg.BaseService import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.database.Profile import com.github.shadowsocks.database.Profile
...@@ -54,7 +53,6 @@ import com.github.shadowsocks.plugin.showAllowingStateLoss ...@@ -54,7 +53,6 @@ import com.github.shadowsocks.plugin.showAllowingStateLoss
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.Action import com.github.shadowsocks.utils.Action
import com.github.shadowsocks.utils.datas import com.github.shadowsocks.utils.datas
import com.github.shadowsocks.utils.printLog
import com.github.shadowsocks.utils.readableMessage import com.github.shadowsocks.utils.readableMessage
import com.github.shadowsocks.widget.ListHolderListener import com.github.shadowsocks.widget.ListHolderListener
import com.github.shadowsocks.widget.MainListListener import com.github.shadowsocks.widget.MainListListener
...@@ -69,6 +67,7 @@ import com.google.zxing.MultiFormatWriter ...@@ -69,6 +67,7 @@ import com.google.zxing.MultiFormatWriter
import com.google.zxing.WriterException import com.google.zxing.WriterException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import timber.log.Timber
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener { class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
...@@ -146,7 +145,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener { ...@@ -146,7 +145,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
}) })
} }
} catch (e: WriterException) { } catch (e: WriterException) {
Crashlytics.logException(e) Timber.w(e)
(activity as MainActivity).snackbar().setText(e.readableMessage).show() (activity as MainActivity).snackbar().setText(e.readableMessage).show()
dismiss() dismiss()
null null
...@@ -515,7 +514,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener { ...@@ -515,7 +514,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
return true return true
} }
} catch (exc: Exception) { } catch (exc: Exception) {
exc.printStackTrace() Timber.d(exc)
} }
(activity as MainActivity).snackbar().setText(R.string.action_import_err).show() (activity as MainActivity).snackbar().setText(R.string.action_import_err).show()
true true
...@@ -600,7 +599,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener { ...@@ -600,7 +599,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
it.write(profiles.toString(2)) it.write(profiles.toString(2))
} }
} catch (e: Exception) { } catch (e: Exception) {
printLog(e) Timber.w(e)
(activity as MainActivity).snackbar(e.readableMessage).show() (activity as MainActivity).snackbar(e.readableMessage).show()
} }
} }
......
...@@ -29,7 +29,6 @@ import android.hardware.camera2.CameraManager ...@@ -29,7 +29,6 @@ import android.hardware.camera2.CameraManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.util.SparseArray import android.util.SparseArray
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
...@@ -37,21 +36,23 @@ import android.widget.Toast ...@@ -37,21 +36,23 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.util.forEach import androidx.core.util.forEach
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.database.Profile import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.utils.* import com.github.shadowsocks.utils.datas
import com.github.shadowsocks.utils.forEachTry
import com.github.shadowsocks.utils.openBitmap
import com.github.shadowsocks.utils.readableMessage
import com.google.android.gms.common.GoogleApiAvailability import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.samples.vision.barcodereader.BarcodeCapture import com.google.android.gms.samples.vision.barcodereader.BarcodeCapture
import com.google.android.gms.samples.vision.barcodereader.BarcodeGraphic import com.google.android.gms.samples.vision.barcodereader.BarcodeGraphic
import com.google.android.gms.vision.Frame import com.google.android.gms.vision.Frame
import com.google.android.gms.vision.barcode.Barcode import com.google.android.gms.vision.barcode.Barcode
import com.google.android.gms.vision.barcode.BarcodeDetector import com.google.android.gms.vision.barcode.BarcodeDetector
import timber.log.Timber
import xyz.belvi.mobilevisionbarcodescanner.BarcodeRetriever import xyz.belvi.mobilevisionbarcodescanner.BarcodeRetriever
class ScannerActivity : AppCompatActivity(), BarcodeRetriever { class ScannerActivity : AppCompatActivity(), BarcodeRetriever {
companion object { companion object {
private const val TAG = "ScannerActivity"
private const val REQUEST_IMPORT = 2 private const val REQUEST_IMPORT = 2
private const val REQUEST_IMPORT_OR_FINISH = 3 private const val REQUEST_IMPORT_OR_FINISH = 3
private const val REQUEST_GOOGLE_API = 4 private const val REQUEST_GOOGLE_API = 4
...@@ -109,7 +110,7 @@ class ScannerActivity : AppCompatActivity(), BarcodeRetriever { ...@@ -109,7 +110,7 @@ class ScannerActivity : AppCompatActivity(), BarcodeRetriever {
} }
override fun onRetrievedMultiple(closetToClick: Barcode?, barcode: MutableList<BarcodeGraphic>?) = check(false) override fun onRetrievedMultiple(closetToClick: Barcode?, barcode: MutableList<BarcodeGraphic>?) = check(false)
override fun onBitmapScanned(sparseArray: SparseArray<Barcode>?) { } override fun onBitmapScanned(sparseArray: SparseArray<Barcode>?) { }
override fun onRetrievedFailed(reason: String?) = Crashlytics.log(Log.WARN, TAG, reason) override fun onRetrievedFailed(reason: String?) = Timber.w(reason)
override fun onPermissionRequestDenied() { override fun onPermissionRequestDenied() {
Toast.makeText(this, R.string.add_profile_scanner_permission_required, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.add_profile_scanner_permission_required, Toast.LENGTH_SHORT).show()
startImport() startImport()
......
...@@ -32,7 +32,10 @@ import android.os.Parcelable ...@@ -32,7 +32,10 @@ import android.os.Parcelable
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.view.* import android.view.*
import android.widget.* import android.widget.AdapterView
import android.widget.EditText
import android.widget.Spinner
import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
...@@ -57,6 +60,7 @@ import com.github.shadowsocks.widget.UndoSnackbarManager ...@@ -57,6 +60,7 @@ import com.github.shadowsocks.widget.UndoSnackbarManager
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
import me.zhanghai.android.fastscroll.FastScrollerBuilder import me.zhanghai.android.fastscroll.FastScrollerBuilder
import timber.log.Timber
import java.net.IDN import java.net.IDN
import java.net.MalformedURLException import java.net.MalformedURLException
import java.net.URL import java.net.URL
...@@ -449,7 +453,7 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener, ...@@ -449,7 +453,7 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
check(adapter.addToProxy(clipboard.primaryClip!!.getItemAt(0).text.toString()) != null) check(adapter.addToProxy(clipboard.primaryClip!!.getItemAt(0).text.toString()) != null)
} catch (exc: Exception) { } catch (exc: Exception) {
(activity as MainActivity).snackbar().setText(R.string.action_import_err).show() (activity as MainActivity).snackbar().setText(R.string.action_import_err).show()
exc.printStackTrace() Timber.d(exc)
} }
true true
} }
......
...@@ -4,8 +4,8 @@ import java.util.regex.Pattern ...@@ -4,8 +4,8 @@ import java.util.regex.Pattern
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'com.google.android.gms.oss-licenses-plugin' apply plugin: 'com.google.android.gms.oss-licenses-plugin'
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
apply plugin: 'io.fabric'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
......
...@@ -29,13 +29,11 @@ import android.os.Bundle ...@@ -29,13 +29,11 @@ import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.RemoteException import android.os.RemoteException
import android.text.format.Formatter import android.text.format.Formatter
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.leanback.preference.LeanbackPreferenceFragmentCompat import androidx.leanback.preference.LeanbackPreferenceFragmentCompat
import androidx.lifecycle.observe import androidx.lifecycle.observe
import androidx.preference.* import androidx.preference.*
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.BootReceiver import com.github.shadowsocks.BootReceiver
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.aidl.IShadowsocksService import com.github.shadowsocks.aidl.IShadowsocksService
...@@ -50,9 +48,9 @@ import com.github.shadowsocks.preference.HostsSummaryProvider ...@@ -50,9 +48,9 @@ import com.github.shadowsocks.preference.HostsSummaryProvider
import com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener import com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener
import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.datas import com.github.shadowsocks.utils.datas
import com.github.shadowsocks.utils.printLog
import com.github.shadowsocks.utils.readableMessage import com.github.shadowsocks.utils.readableMessage
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
import timber.log.Timber
class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksConnection.Callback, class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksConnection.Callback,
OnPreferenceDataStoreChangeListener { OnPreferenceDataStoreChangeListener {
...@@ -61,7 +59,6 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo ...@@ -61,7 +59,6 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo
private const val REQUEST_REPLACE_PROFILES = 2 private const val REQUEST_REPLACE_PROFILES = 2
private const val REQUEST_EXPORT_PROFILES = 3 private const val REQUEST_EXPORT_PROFILES = 3
private const val REQUEST_HOSTS = 4 private const val REQUEST_HOSTS = 4
private const val TAG = "MainPreferenceFragment"
} }
private lateinit var fab: ListPreference private lateinit var fab: ListPreference
...@@ -266,7 +263,7 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo ...@@ -266,7 +263,7 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo
when (requestCode) { when (requestCode) {
REQUEST_CONNECT -> if (resultCode == Activity.RESULT_OK) Core.startService() else { REQUEST_CONNECT -> if (resultCode == Activity.RESULT_OK) Core.startService() else {
Toast.makeText(requireContext(), R.string.vpn_permission_denied, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), R.string.vpn_permission_denied, Toast.LENGTH_SHORT).show()
Crashlytics.log(Log.ERROR, TAG, "Failed to start VpnService from onActivityResult: $data") Timber.e("Failed to start VpnService from onActivityResult: $data")
} }
REQUEST_REPLACE_PROFILES -> { REQUEST_REPLACE_PROFILES -> {
if (resultCode != Activity.RESULT_OK) return if (resultCode != Activity.RESULT_OK) return
...@@ -276,7 +273,7 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo ...@@ -276,7 +273,7 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo
context.contentResolver.openInputStream(it) context.contentResolver.openInputStream(it)
}.filterNotNull(), true) }.filterNotNull(), true)
} catch (e: Exception) { } catch (e: Exception) {
printLog(e) Timber.w(e)
Toast.makeText(context, e.readableMessage, Toast.LENGTH_SHORT).show() Toast.makeText(context, e.readableMessage, Toast.LENGTH_SHORT).show()
} }
populateProfiles() populateProfiles()
...@@ -290,7 +287,7 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo ...@@ -290,7 +287,7 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo
it.write(profiles.toString(2)) it.write(profiles.toString(2))
} }
} catch (e: Exception) { } catch (e: Exception) {
printLog(e) Timber.w(e)
Toast.makeText(context, e.readableMessage, Toast.LENGTH_SHORT).show() Toast.makeText(context, e.readableMessage, Toast.LENGTH_SHORT).show()
} }
} }
......
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