diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt
index e218225b1..3d26048fd 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt
@@ -26,6 +26,9 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
         TODO("not implemented")
     }
 
+    override val isOnline: Boolean
+        get() = true
+
     override suspend fun queryProfile(): Profile {
         TODO("not implemented")
     }
@@ -40,11 +43,16 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
 
 }
 
-internal class MemberImpl(bot: QQAndroidBot, group: Group, override val coroutineContext: CoroutineContext, override val id: Long) : ContactImpl(), Member {
-    override val group: Group by group.unsafeWeakRef()
+internal class MemberImpl(
+    qq: QQImpl,
+    group: GroupImpl,
+    override val coroutineContext: CoroutineContext
+) : ContactImpl(), Member, QQ by qq {
+    override val group: GroupImpl by group.unsafeWeakRef()
+    val qq: QQImpl by qq.unsafeWeakRef()
+
     override val permission: MemberPermission
         get() = TODO("not implemented")
-    override val bot: QQAndroidBot by bot.unsafeWeakRef()
 
     override suspend fun mute(durationSeconds: Int): Boolean {
         TODO("not implemented")
@@ -54,26 +62,6 @@ internal class MemberImpl(bot: QQAndroidBot, group: Group, override val coroutin
         TODO("not implemented")
     }
 
-    override suspend fun queryProfile(): Profile {
-        TODO("not implemented")
-    }
-
-    override suspend fun queryPreviousNameList(): PreviousNameList {
-        TODO("not implemented")
-    }
-
-    override suspend fun queryRemark(): FriendNameRemark {
-        TODO("not implemented")
-    }
-
-    override suspend fun sendMessage(message: MessageChain) {
-        TODO("not implemented")
-    }
-
-    override suspend fun uploadImage(image: ExternalImage): ImageId {
-        TODO("not implemented")
-    }
-
 }
 
 
@@ -89,7 +77,7 @@ internal class GroupImpl(bot: QQAndroidBot, override val coroutineContext: Corou
     override val members: ContactList<Member> = ContactList(LockFreeLinkedList())
 
     override fun getMember(id: Long): Member =
-        members.delegate.filteringGetOrAdd({ it.id == id }, { MemberImpl(bot as QQAndroidBot, this, coroutineContext, id) })
+        members.delegate.filteringGetOrAdd({ it.id == id }, { MemberImpl(bot.getQQ(id) as QQImpl, this, coroutineContext) })
 
     override suspend fun updateGroupInfo(): GroupInfo {
         TODO("not implemented")
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
index f2d8b026a..0db9bec14 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
@@ -14,17 +14,11 @@ import net.mamoe.mirai.event.broadcast
 import net.mamoe.mirai.network.BotNetworkHandler
 import net.mamoe.mirai.qqandroid.QQAndroidBot
 import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent
-import net.mamoe.mirai.qqandroid.network.protocol.data.jce.GetFriendListReq
-import net.mamoe.mirai.qqandroid.network.protocol.packet.KnownPacketFactories
-import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
-import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory
-import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketLogger
-import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
+import net.mamoe.mirai.qqandroid.network.protocol.packet.*
 import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket
 import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket.LoginPacketResponse.*
 import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc
 import net.mamoe.mirai.utils.*
-import net.mamoe.mirai.utils.cryptor.contentToString
 import net.mamoe.mirai.utils.io.*
 import kotlin.coroutines.CoroutineContext
 
@@ -56,7 +50,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
 
                 is Captcha -> when (response) {
                     is Captcha.Picture -> {
-                        var result = bot.configuration.loginSolver.onSolvePicCaptcha(bot, response.data)
+                        var result = response.data.withUse {
+                            bot.configuration.loginSolver.onSolvePicCaptcha(bot, this)
+                        }
                         if (result == null || result.length != 4) {
                             //refresh captcha
                             result = "ABCD"
@@ -325,19 +321,33 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
     /**
      * 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms)
      */
-    suspend fun <E : Packet> OutgoingPacket.sendAndExpect(timoutMillis: Long = 3000): E {
+    suspend fun <E : Packet> OutgoingPacket.sendAndExpect(timeoutMillis: Long = 3000, retry: Int = 1): E {
+        require(timeoutMillis > 0) { "timeoutMillis must > 0" }
+        require(retry >= 0) { "retry must >= 0" }
+
         val handler = PacketListener(commandName = commandName, sequenceId = sequenceId)
         packetListeners.addLast(handler)
         bot.logger.info("Send: ${this.commandName}")
+        var lastException: Exception? = null
+        repeat(retry + 1) {
+            try {
+                return doSendAndReceive(timeoutMillis, handler)
+            } catch (e: Exception) {
+                lastException = e
+            }
+        }
+        throw lastException!!
+    }
+
+    private suspend inline fun <E : Packet> OutgoingPacket.doSendAndReceive(timeoutMillis: Long = 3000, handler: PacketListener): E {
         channel.send(delegate)
-        return withTimeoutOrNull(timoutMillis) {
+        return withTimeoutOrNull(timeoutMillis) {
             @Suppress("UNCHECKED_CAST")
             handler.await() as E
-
             // 不要 `withTimeout`. timeout 的异常会不知道去哪了.
         } ?: net.mamoe.mirai.qqandroid.utils.inline {
             packetListeners.remove(handler)
-            error("timeout when sending ${commandName}")
+            error("timeout when sending $commandName")
         }
     }
 
diff --git a/mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network/ContactImpl.kt b/mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network/ContactImpl.kt
index b648de090..dcf769fe9 100644
--- a/mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network/ContactImpl.kt
+++ b/mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network/ContactImpl.kt
@@ -139,6 +139,9 @@ internal class QQImpl @PublishedApi internal constructor(bot: TIMPCBot, override
         }
     }
 
+    override val isOnline: Boolean
+        get() = true
+
     override suspend fun queryProfile(): Profile = withTIMPCBot {
         RequestProfileDetailsPacket(bot.uin, id, sessionKey).sendAndExpect<RequestProfileDetailsResponse>().profile
     }
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/defaultLoginSolver.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/defaultLoginSolver.kt
index dc631dfc0..c58779c47 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/defaultLoginSolver.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/defaultLoginSolver.kt
@@ -15,12 +15,7 @@ actual var defaultLoginSolver: LoginSolver = object : LoginSolver() {
         error("should be implemented manually by you")
     }
 
-    override suspend fun onGetPhoneNumber(): String {
+    override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? {
         error("should be implemented manually by you")
     }
-
-    override suspend fun onGetSMSVerifyCode(): String {
-        error("should be implemented manually by you")
-    }
-
 }
\ No newline at end of file
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
index b4fdd1340..fbfdac520 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
@@ -34,6 +34,14 @@ actual class PlatformSocket : Closeable {
     @PublishedApi
     internal lateinit var readChannel: ByteReadChannel
 
+    actual suspend inline fun send(packet: ByteArray, offset: Int, length: Int) {
+        try {
+            writeChannel.writeFully(packet, offset, length)
+        } catch (e: Exception) {
+            throw SendPacketInternalException(e)
+        }
+    }
+
     /**
      * @throws SendPacketInternalException
      */
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
index f334e4c40..061420ffb 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
@@ -98,7 +98,15 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
         }
         _network = createNetworkHandler(this.coroutineContext)
 
-        _network.login()
+        while (true){
+            try {
+                return _network.login()
+            } catch (e: Exception){
+                e.logStacktrace("Exception when login")
+            }
+            delay(3000)
+            logger.warning("Login failed. Retrying in 3s...")
+        }
     }
 
     protected abstract fun createNetworkHandler(coroutineContext: CoroutineContext): N
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt
index 7fea786a3..b7f8b823f 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt
@@ -13,6 +13,7 @@ interface Member : QQ, Contact {
     /**
      * 所在的群
      */
+    @WeakRefProperty
     val group: Group
 
     /**
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt
index c7dfcfe23..289dcef79 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt
@@ -21,10 +21,15 @@ import net.mamoe.mirai.data.Profile
  * @author Him188moe
  */
 interface QQ : Contact, CoroutineScope {
+    /**
+     * 是否在线. 这个属性的值将会与服务器同步更新.
+     */
+    val isOnline: Boolean
+
     /**
      * 请求头像下载链接
      */
-   // @MiraiExperimentalAPI
+    // @MiraiExperimentalAPI
     //suspend fun queryAvatar(): AvatarLink
 
     /**
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformSocket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformSocket.kt
index ed9a245c2..d3eb05c20 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformSocket.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformSocket.kt
@@ -12,6 +12,11 @@ import net.mamoe.mirai.utils.MiraiInternalAPI
 expect class PlatformSocket() : Closeable {
     suspend fun connect(serverHost: String, serverPort: Int)
 
+    /**
+     * @throws SendPacketInternalException
+     */
+    suspend inline fun send(packet: ByteArray, offset: Int = 0, length: Int = packet.size - offset)
+
     /**
      * @throws SendPacketInternalException
      */
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/DefaultCaptchaSolverJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/DefaultCaptchaSolverJvm.kt
index 4266a8898..6bcf26598 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/DefaultCaptchaSolverJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/DefaultCaptchaSolverJvm.kt
@@ -29,36 +29,34 @@ actual var defaultLoginSolver: LoginSolver = DefaultLoginSolver()
 
 
 class DefaultLoginSolver : LoginSolver() {
-    override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? {
-        loginSolverLock.withLock {
-            val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() }
-            withContext(Dispatchers.IO) {
-                tempFile.createNewFile()
-                bot.logger.info("需要图片验证码登录, 验证码为 4 字母")
-                try {
-                    tempFile.writeChannel().use { writeFully(data) }
-                    bot.logger.info("将会显示字符图片. 若看不清字符图片, 请查看文件 ${tempFile.absolutePath}")
-                } catch (e: Exception) {
-                    bot.logger.info("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片")
-                }
+    override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? = loginSolverLock.withLock {
+        val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() }
+        withContext(Dispatchers.IO) {
+            tempFile.createNewFile()
+            bot.logger.info("需要图片验证码登录, 验证码为 4 字母")
+            try {
+                tempFile.writeChannel().use { writeFully(data) }
+                bot.logger.info("将会显示字符图片. 若看不清字符图片, 请查看文件 ${tempFile.absolutePath}")
+            } catch (e: Exception) {
+                bot.logger.info("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片")
+            }
 
-                tempFile.inputStream().use {
-                    val img = ImageIO.read(it)
-                    if (img == null) {
-                        bot.logger.info("无法创建字符图片. 请查看文件")
-                    } else {
-                        bot.logger.info(img.createCharImg())
-                    }
+            tempFile.inputStream().use {
+                val img = ImageIO.read(it)
+                if (img == null) {
+                    bot.logger.info("无法创建字符图片. 请查看文件")
+                } else {
+                    bot.logger.info(img.createCharImg())
                 }
             }
-            bot.logger.info("请输入 4 位字母验证码. 若要更换验证码, 请直接回车")
-            return readLine()?.takeUnless { it.isEmpty() || it.length != 4 }.also {
-                bot.logger.info("正在提交[" + it +"]中...")
-            }
+        }
+        bot.logger.info("请输入 4 位字母验证码. 若要更换验证码, 请直接回车")
+        return readLine()?.takeUnless { it.isEmpty() || it.length != 4 }.also {
+            bot.logger.info("正在提交[$it]中...")
         }
     }
 
-    override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? {
+    override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? = loginSolverLock.withLock {
         bot.logger.info("需要滑动验证码")
         bot.logger.info("请在任意浏览器中打开以下链接并完成验证码. ")
         bot.logger.info("完成后请输入任意字符 ")
@@ -68,7 +66,7 @@ class DefaultLoginSolver : LoginSolver() {
         }
     }
 
-    override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? {
+    override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? = loginSolverLock.withLock {
         bot.logger.info("需要进行账户安全认证")
         bot.logger.info("该账户有[设备锁]/[不常用登陆地点]/[不常用设备登陆]的问题")
         bot.logger.info("完成以下账号认证即可成功登陆|理论本认证在mirai每个账户中最多出现1次")
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
index b4fdd1340..fbfdac520 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
@@ -34,6 +34,14 @@ actual class PlatformSocket : Closeable {
     @PublishedApi
     internal lateinit var readChannel: ByteReadChannel
 
+    actual suspend inline fun send(packet: ByteArray, offset: Int, length: Int) {
+        try {
+            writeChannel.writeFully(packet, offset, length)
+        } catch (e: Exception) {
+            throw SendPacketInternalException(e)
+        }
+    }
+
     /**
      * @throws SendPacketInternalException
      */
diff --git a/mirai-demos/mirai-demo-android/src/main/kotlin/net/mamoe/mirai/demo/MiraiService.kt b/mirai-demos/mirai-demo-android/src/main/kotlin/net/mamoe/mirai/demo/MiraiService.kt
index 5574d630b..1492693fe 100644
--- a/mirai-demos/mirai-demo-android/src/main/kotlin/net/mamoe/mirai/demo/MiraiService.kt
+++ b/mirai-demos/mirai-demo-android/src/main/kotlin/net/mamoe/mirai/demo/MiraiService.kt
@@ -57,11 +57,7 @@ class MiraiService : Service() {
                         TODO("not implemented")
                     }
 
-                    override suspend fun onGetPhoneNumber(): String {
-                        TODO("not implemented")
-                    }
-
-                    override suspend fun onGetSMSVerifyCode(): String {
+                    override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? {
                         TODO("not implemented")
                     }