[WIP] refactor service

This commit is contained in:
Kr328 committed 2020-01-16 23:42:10 +08:00
1 parent a44bb05345
commit fa6ac580d0
16 files changed
+495 -81

No files matched your search

@@ -3,10 +3,7 @@ package com.github.kr328.clash.core
import android.content.Context
import bridge.Bridge
import bridge.EventPoll
import com.github.kr328.clash.core.event.BandwidthEvent
import com.github.kr328.clash.core.event.LogEvent
import com.github.kr328.clash.core.event.ProcessEvent
import com.github.kr328.clash.core.event.TrafficEvent
import com.github.kr328.clash.core.event.*
import com.github.kr328.clash.core.model.General
import com.github.kr328.clash.core.model.Proxy
import com.github.kr328.clash.core.model.ProxyGroup
@@ -15,6 +12,7 @@ import com.github.kr328.clash.core.transact.ProxyCollectionImpl
import com.github.kr328.clash.core.transact.ProxyGroupCollectionImpl
import java.io.InputStream
import java.lang.IllegalStateException
import java.util.concurrent.BrokenBarrierException
import java.util.concurrent.CompletableFuture
class Clash(
@@ -41,7 +39,7 @@ class Clash(
}
fun start() {
if ( currentProcess == ProcessEvent.STARTED )
if (currentProcess == ProcessEvent.STARTED)
return
currentProcess = ProcessEvent.STARTED
@@ -52,7 +50,7 @@ class Clash(
}
fun stop() {
if ( currentProcess == ProcessEvent.STOPPED )
if (currentProcess == ProcessEvent.STOPPED)
return
currentProcess = ProcessEvent.STOPPED
@@ -86,7 +84,7 @@ class Clash(
fun downloadProfile(url: String, output: String, baseDir: String): CompletableFuture<Unit> {
return DoneCallbackImpl().apply {
Bridge.downloadProfileAndCheck(url, output, baseDir,this)
Bridge.downloadProfileAndCheck(url, output, baseDir, this)
}
}
@@ -133,30 +131,49 @@ class Clash(
fun queryGeneral(): General {
val t = Bridge.queryGeneral()
return General(General.Mode.fromString(t.mode),
t.httpPort.toInt(), t.socksPort.toInt(), t.redirectPort.toInt())
return General(
General.Mode.fromString(t.mode),
t.httpPort.toInt(), t.socksPort.toInt(), t.redirectPort.toInt()
)
}
fun pollTraffic(onEvent: (TrafficEvent) -> Unit): Poll {
return Poll(Bridge.pollTraffic { down, up ->
onEvent(TrafficEvent(down, up))
})
fun openTrafficEvent(): EventStream<TrafficEvent> {
return object: EventStream<TrafficEvent>() {
val traffic = Bridge.pollTraffic { down, up ->
send(TrafficEvent(down, up))
}
override fun onClose() {
traffic.stop()
}
}
}
fun pollBandwidth(onEvent: (BandwidthEvent) -> Unit): Poll {
return Poll(Bridge.pollBandwidth {
onEvent(BandwidthEvent(it))
})
fun openBandwidthEvent(): EventStream<BandwidthEvent> {
return object: EventStream<BandwidthEvent>() {
val bandwidth = Bridge.pollBandwidth {
send(BandwidthEvent(it))
}
override fun onClose() {
bandwidth.stop()
}
}
}
fun pollLogs(onEvent: (LogEvent) -> Unit): Poll {
return Poll(Bridge.pollLogs { level, payload ->
onEvent(LogEvent(LogEvent.Level.fromString(level), payload))
})
fun openLogEvent(): EventStream<LogEvent> {
return object: EventStream<LogEvent>() {
val log = Bridge.pollLogs { level, payload ->
send(LogEvent(LogEvent.Level.fromString(level), payload))
}
override fun onClose() {
log.stop()
}
}
}
private fun enforceStarted() {
if ( currentProcess == ProcessEvent.STOPPED )
throw IllegalStateException("Clash Stopped")
check(currentProcess != ProcessEvent.STOPPED) { "Clash Stopped" }
}
}
@@ -6,7 +6,7 @@ import com.github.kr328.clash.core.serialization.Parcels
import kotlinx.serialization.Serializable
@Serializable
data class BandwidthEvent(val total: Long): Parcelable {
data class BandwidthEvent(val total: Long): Event {
override fun writeToParcel(parcel: Parcel, flags: Int) {
Parcels.dump(serializer(), this, parcel)
}
@@ -1,6 +1,8 @@
package com.github.kr328.clash.core.event
interface Event {
import android.os.Parcelable
interface Event: Parcelable {
companion object {
const val EVENT_LOG = 1
const val EVENT_TRAFFIC = 3
@@ -0,0 +1,21 @@
package com.github.kr328.clash.core.event
import android.os.Parcelable
abstract class EventStream<T: Event> {
private var on: (T) -> Unit = {}
fun onEvent(callback: (T) -> Unit) {
on = callback
}
fun close() {
onClose()
}
fun send(event: T) {
on(event)
}
abstract fun onClose()
}
@@ -8,7 +8,7 @@ import kotlinx.serialization.internal.StringDescriptor
@Serializable
data class LogEvent(val level: Level, val message: String, val time: Long = System.currentTimeMillis()) :
Event, Parcelable {
Event {
companion object {
const val DEBUG_VALUE = "debug"
const val INFO_VALUE = "info"
@@ -1,5 +1,9 @@
package com.github.kr328.clash.core.model
import android.os.Parcel
import android.os.Parcelable
import com.github.kr328.clash.core.serialization.MergedParcels
import com.github.kr328.clash.core.serialization.Parcels
import kotlinx.serialization.Serializable
@Serializable
@@ -9,4 +13,22 @@ data class ProxyGroup(
val delay: Long,
val current: String,
val proxies: List<Proxy>
)
): Parcelable {
override fun writeToParcel(parcel: Parcel, flags: Int) {
MergedParcels.dump(serializer(), this, parcel)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<ProxyGroup> {
override fun createFromParcel(parcel: Parcel): ProxyGroup {
return MergedParcels.load(serializer(), parcel)
}
override fun newArray(size: Int): Array<ProxyGroup?> {
return arrayOfNulls(size)
}
}
}
@@ -0,0 +1,218 @@
package com.github.kr328.clash.core.serialization
import android.os.Parcel
import kotlinx.serialization.*
import kotlinx.serialization.modules.EmptyModule
import kotlinx.serialization.modules.SerialModule
object MergedParcels: AbstractSerialFormat(EmptyModule) {
fun <T> dump(serializer: SerializationStrategy<T>, obj: T, parcel: Parcel) {
val data = Parcel.obtain()
val encoder = ParcelsEncoder(data)
try {
serializer.serialize(encoder, obj)
data.setDataPosition(0)
parcel.writeStringList(encoder.getStringList())
parcel.appendFrom(data, 0, data.dataSize())
}
finally {
data.recycle()
}
}
fun <T> load(deserializer: DeserializationStrategy<T>, parcel: Parcel): T {
val strings = mutableListOf<String>().apply { parcel.readStringList(this) }
return deserializer.deserialize(ParcelsDecoder(strings, parcel))
}
private class ParcelsEncoder(private val parcel: Parcel) :
Encoder, CompositeEncoder {
private val strings = mutableMapOf<String, Int>()
private var stringIndex = 0
fun getStringList(): List<String> {
val result = mutableListOf<String>()
strings.map { it.value to it.key }
.sortedBy { it.first }
.forEach { result.add(it.second) }
return result
}
override val context: SerialModule
get() = EmptyModule
override fun beginCollection(
desc: SerialDescriptor,
collectionSize: Int,
vararg typeParams: KSerializer<*>
): CompositeEncoder {
encodeInt(collectionSize)
return super.beginCollection(desc, collectionSize, *typeParams)
}
override fun encodeBooleanElement(desc: SerialDescriptor, index: Int, value: Boolean) =
encodeBoolean(value)
override fun encodeByteElement(desc: SerialDescriptor, index: Int, value: Byte) =
encodeByte(value)
override fun encodeCharElement(desc: SerialDescriptor, index: Int, value: Char) =
encodeChar(value)
override fun encodeDoubleElement(desc: SerialDescriptor, index: Int, value: Double) =
encodeDouble(value)
override fun encodeFloatElement(desc: SerialDescriptor, index: Int, value: Float) =
encodeFloat(value)
override fun encodeIntElement(desc: SerialDescriptor, index: Int, value: Int) =
encodeInt(value)
override fun encodeLongElement(desc: SerialDescriptor, index: Int, value: Long) =
encodeLong(value)
override fun encodeShortElement(desc: SerialDescriptor, index: Int, value: Short) =
encodeShort(value)
override fun encodeStringElement(desc: SerialDescriptor, index: Int, value: String) =
encodeString(value)
override fun encodeUnitElement(desc: SerialDescriptor, index: Int) =
encodeUnit()
override fun encodeNonSerializableElement(desc: SerialDescriptor, index: Int, value: Any) =
throw IllegalArgumentException("Unsupported")
override fun <T : Any> encodeNullableSerializableElement(
desc: SerialDescriptor,
index: Int,
serializer: SerializationStrategy<T>,
value: T?
) = encodeNullableSerializableValue(serializer, value)
override fun <T> encodeSerializableElement(
desc: SerialDescriptor,
index: Int,
serializer: SerializationStrategy<T>,
value: T
) = encodeSerializableValue(serializer, value)
override fun beginStructure(
desc: SerialDescriptor,
vararg typeParams: KSerializer<*>
): CompositeEncoder = this
override fun encodeBoolean(value: Boolean) =
parcel.writeByte(if ( value ) 1 else 0)
override fun encodeByte(value: Byte) =
parcel.writeByte(value)
override fun encodeChar(value: Char) =
parcel.writeInt(value.toInt())
override fun encodeDouble(value: Double) =
parcel.writeDouble(value)
override fun encodeEnum(enumDescription: SerialDescriptor, ordinal: Int) =
parcel.writeInt(ordinal)
override fun encodeFloat(value: Float) =
parcel.writeFloat(value)
override fun encodeInt(value: Int) =
parcel.writeInt(value)
override fun encodeLong(value: Long) =
parcel.writeLong(value)
override fun encodeNotNullMark() =
encodeBoolean(true)
override fun encodeNull() =
encodeBoolean(false)
override fun encodeShort(value: Short) =
parcel.writeInt(value.toInt())
override fun encodeUnit() {}
override fun encodeString(value: String) {
val index = strings.computeIfAbsent(value) {
stringIndex++
}
parcel.writeInt(index)
}
}
class ParcelsDecoder(private val strings: List<String>, private val parcel: Parcel) : Decoder, CompositeDecoder {
override val context: SerialModule
get() = EmptyModule
override val updateMode: UpdateMode
get() = UpdateMode.BANNED
override fun decodeElementIndex(desc: SerialDescriptor) =
CompositeDecoder.READ_ALL
override fun decodeCollectionSize(desc: SerialDescriptor) =
decodeInt()
override fun decodeBooleanElement(desc: SerialDescriptor, index: Int) =
decodeBoolean()
override fun decodeByteElement(desc: SerialDescriptor, index: Int) =
decodeByte()
override fun decodeCharElement(desc: SerialDescriptor, index: Int) =
decodeChar()
override fun decodeDoubleElement(desc: SerialDescriptor, index: Int) =
decodeDouble()
override fun decodeFloatElement(desc: SerialDescriptor, index: Int) =
decodeFloat()
override fun decodeIntElement(desc: SerialDescriptor, index: Int) =
decodeInt()
override fun decodeShortElement(desc: SerialDescriptor, index: Int) =
decodeShort()
override fun decodeLongElement(desc: SerialDescriptor, index: Int) =
decodeLong()
override fun decodeStringElement(desc: SerialDescriptor, index: Int) =
decodeString()
override fun decodeUnitElement(desc: SerialDescriptor, index: Int) =
decodeUnit()
override fun <T : Any> decodeNullableSerializableElement(
desc: SerialDescriptor,
index: Int,
deserializer: DeserializationStrategy<T?>
) = decodeNullableSerializableValue(deserializer)
override fun <T> decodeSerializableElement(
desc: SerialDescriptor,
index: Int,
deserializer: DeserializationStrategy<T>
) = decodeSerializableValue(deserializer)
override fun <T : Any> updateNullableSerializableElement(
desc: SerialDescriptor,
index: Int,
deserializer: DeserializationStrategy<T?>,
old: T?
) = updateNullableSerializableValue(deserializer, old)
override fun <T> updateSerializableElement(
desc: SerialDescriptor,
index: Int,
deserializer: DeserializationStrategy<T>,
old: T
) = updateSerializableValue(deserializer, old)
override fun beginStructure(
desc: SerialDescriptor,
vararg typeParams: KSerializer<*>
): CompositeDecoder = this
override fun decodeBoolean() =
parcel.readByte() != 0.toByte()
override fun decodeByte() =
parcel.readByte()
override fun decodeChar() =
parcel.readInt().toChar()
override fun decodeDouble() =
parcel.readDouble()
override fun decodeEnum(enumDescription: SerialDescriptor) =
parcel.readInt()
override fun decodeFloat() =
parcel.readFloat()
override fun decodeInt() =
parcel.readInt()
override fun decodeLong() =
parcel.readLong()
override fun decodeNotNullMark() =
decodeBoolean()
override fun decodeNull() =
null
override fun decodeShort() =
parcel.readInt().toShort()
override fun decodeUnit() {}
override fun decodeString() =
strings[parcel.readInt()]
}
}
@@ -1,5 +0,0 @@
package com.github.kr328.clash.callback;
interface IUrlTestCallback {
void onResult(String proxy, long delay);
}
@@ -1,13 +0,0 @@
package com.github.kr328.clash.service;
import com.github.kr328.clash.core.event.Event;
interface IClashEventObserver {
void onProcessEvent(in ProcessEvent event);
void onLogEvent(in LogEvent event);
void onErrorEvent(in ErrorEvent event);
void onTrafficEvent(in TrafficEvent event);
void onBandwidthEvent(in BandwidthEvent event);
void onProfileChanged(in ProfileChangedEvent event);
void onProfileReloaded(in ProfileReloadEvent event);
}
@@ -1,8 +0,0 @@
package com.github.kr328.clash.service;
import com.github.kr328.clash.service.IClashEventObserver;
interface IClashEventService {
void registerEventObserver(String id, in IClashEventObserver observer, in int[] events);
void unregisterEventObserver(String id);
}
@@ -4,29 +4,17 @@ import com.github.kr328.clash.service.IClashEventObserver;
import com.github.kr328.clash.service.IClashEventService;
import com.github.kr328.clash.service.IClashProfileService;
import com.github.kr328.clash.service.IClashSettingService;
import com.github.kr328.clash.service.ipc.IPCParcelables;
import com.github.kr328.clash.callback.IUrlTestCallback;
import com.github.kr328.clash.core.event.Event;
import com.github.kr328.clash.core.model.Packet;
interface IClashService {
// Services
IClashEventService getEventService();
IClashProfileService getProfileService();
IClashSettingService getSettingService();
// Status
ProcessEvent getCurrentProcessStatus();
interface IClashManager {
// Control
void setSelectProxy(String proxy, String selected);
void start();
void stop();
ParcelableCompletedFuture setSelectProxy(String proxy, String selected);
// Query
CompressedProxyList queryAllProxies();
General queryGeneral();
void startUrlTest(in String[] proxies, IUrlTestCallback callback);
// Utils
String checkProfileValid(in ParcelFileDescriptor pipe);
ParcelableCompletedFuture startUrlTest(String group);
}
@@ -1,12 +0,0 @@
package com.github.kr328.clash.service;
import com.github.kr328.clash.service.data.ClashProfileEntity;
interface IClashProfileService {
ClashProfileEntity[] queryProfiles();
void setActiveProfile(int id);
void addProfile(in ClashProfileEntity profile);
void removeProfile(int id);
void touchProfile(int id);
ClashProfileEntity queryActiveProfile();
}
@@ -0,0 +1,5 @@
package com.github.kr328.clash.service.ipc;
parcelable ParcelablePipe;
parcelable ParcelableResult;
parcelable ParcelableCompletedFuture;
@@ -0,0 +1,60 @@
package com.github.kr328.clash.service.ipc
import android.os.Parcel
import android.os.Parcelable
import android.os.RemoteException
import java.util.concurrent.CompletableFuture
class ParcelableCompletedFuture(private val pipe: ParcelablePipe = ParcelablePipe()) :
CompletableFuture<Parcelable?>(), Parcelable {
constructor(parcel: Parcel) : this(
parcel.readParcelable<ParcelablePipe>(
ParcelableCompletedFuture::class.java.classLoader
) ?: throw NullPointerException()
) {
pipe.onReceive {
val result = (it ?: throw NullPointerException()) as ParcelableResult
if ( result.exception != null ) {
completeExceptionally(RemoteException(result.exception))
}
else {
complete(result.data)
}
}
}
override fun complete(value: Parcelable?): Boolean {
if ( super.complete(value) ) {
pipe.send(ParcelableResult(value, null))
return true
}
return false
}
override fun completeExceptionally(ex: Throwable?): Boolean {
if ( super.completeExceptionally(ex) ) {
pipe.send(ParcelableResult(null, ex?.message ?: "Unknown"))
return true
}
return false
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeParcelable(pipe, 0)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<ParcelableCompletedFuture> {
override fun createFromParcel(parcel: Parcel): ParcelableCompletedFuture {
return ParcelableCompletedFuture(parcel)
}
override fun newArray(size: Int): Array<ParcelableCompletedFuture?> {
return arrayOfNulls(size)
}
}
}
@@ -0,0 +1,89 @@
package com.github.kr328.clash.service.ipc
import android.os.Binder
import android.os.IBinder
import android.os.Parcel
import android.os.Parcelable
class ParcelablePipe() : Parcelable {
private inner class Slave: Binder() {
override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
if ( code == TRANSACT_CODE_SEND_PARCELABLE ) {
receiveCallback(data.readParcelable(ParcelablePipe::class.java.classLoader))
return true
}
return false
}
}
private inner class Master: Binder() {
override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
if ( code == TRANSACT_CODE_SET_SLAVE ) {
slave = data.readStrongBinder()
return true
}
return false
}
}
private var slave: IBinder? = null
private var receiveCallback: (Parcelable?) -> Unit = {}
constructor(parcel: Parcel) : this() {
val master = parcel.readStrongBinder()
val data = Parcel.obtain()
try {
data.writeStrongBinder(Slave())
master.transact(TRANSACT_CODE_SET_SLAVE, data, null, 0)
}
finally {
data.recycle()
}
}
override fun writeToParcel(dest: Parcel?, flags: Int) {
if ( dest == null )
return
dest.writeStrongBinder(Master())
}
override fun describeContents(): Int = 0
fun send(parcelable: Parcelable?): Boolean {
val s = slave ?: return false
val data = Parcel.obtain()
try {
data.writeParcelable(parcelable, 0)
s.transact(TRANSACT_CODE_SEND_PARCELABLE, data, null, 0)
}
finally {
data.recycle()
}
return true
}
fun onReceive(callback: (Parcelable?) -> Unit) {
this.receiveCallback = callback
}
companion object {
private const val TRANSACT_CODE_SET_SLAVE = 1
private const val TRANSACT_CODE_SEND_PARCELABLE = 2
@JvmField
val CREATOR = object : Parcelable.Creator<ParcelablePipe> {
override fun createFromParcel(parcel: Parcel): ParcelablePipe {
return ParcelablePipe(parcel)
}
override fun newArray(size: Int): Array<ParcelablePipe?> {
return arrayOfNulls(size)
}
}
}
}
@@ -0,0 +1,30 @@
package com.github.kr328.clash.service.ipc
import android.os.Parcel
import android.os.Parcelable
data class ParcelableResult(val data: Parcelable?, val exception: String?): Parcelable {
constructor(parcel: Parcel) : this(
parcel.readParcelable(ParcelableResult::class.java.classLoader),
parcel.readString()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeParcelable(data, 0)
parcel.writeString(exception)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<ParcelableResult> {
override fun createFromParcel(parcel: Parcel): ParcelableResult {
return ParcelableResult(parcel)
}
override fun newArray(size: Int): Array<ParcelableResult?> {
return arrayOfNulls(size)
}
}
}