浏览代码

Use wrappers to enforce that withEuiccChannel can't leak references

Peter Cai 1 年之前
父节点
当前提交
76e8fbd56b

+ 6 - 1
app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelManager.kt

@@ -166,7 +166,12 @@ open class DefaultEuiccChannelManager(
     ): R {
         val channel = findEuiccChannelByPortBlocking(physicalSlotId, portId)
             ?: throw EuiccChannelManager.EuiccChannelNotFoundException()
-        return fn(channel)
+        val wrapper = EuiccChannelWrapper(channel)
+        try {
+            return fn(wrapper)
+        } finally {
+            wrapper.invalidateWrapper()
+        }
     }
 
     override suspend fun waitForReconnect(physicalSlotId: Int, portId: Int, timeoutMillis: Long) {

+ 2 - 1
app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt

@@ -68,7 +68,8 @@ interface EuiccChannelManager {
 
     /**
      * Find a EuiccChannel by its slot and port, then run a callback with a reference to it.
-     * The reference is not supposed to be held outside of the callback.
+     * The reference is not supposed to be held outside of the callback. This is enforced via
+     * a wrapper object.
      *
      * If a channel for that slot / port is not found, EuiccChannelNotFoundException is thrown
      */

+ 41 - 0
app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelWrapper.kt

@@ -0,0 +1,41 @@
+package im.angry.openeuicc.core
+
+import im.angry.openeuicc.util.*
+import net.typeblog.lpac_jni.LocalProfileAssistant
+
+class EuiccChannelWrapper(private val _inner: EuiccChannel) : EuiccChannel {
+    private var wrapperInvalidated = false
+
+    private val channel: EuiccChannel
+        get() {
+            if (wrapperInvalidated) {
+                throw IllegalStateException("This wrapper has been invalidated")
+            }
+
+            return _inner
+        }
+    override val port: UiccPortInfoCompat
+        get() = channel.port
+    override val slotId: Int
+        get() = channel.slotId
+    override val logicalSlotId: Int
+        get() = channel.logicalSlotId
+    override val portId: Int
+        get() = channel.portId
+    private val lpaDelegate = lazy {
+        LocalProfileAssistantWrapper(_inner.lpa)
+    }
+    override val lpa: LocalProfileAssistant by lpaDelegate
+    override val valid: Boolean
+        get() = channel.valid
+
+    override fun close() = channel.close()
+
+    fun invalidateWrapper() {
+        wrapperInvalidated = true
+
+        if (lpaDelegate.isInitialized()) {
+            (lpa as LocalProfileAssistantWrapper).invalidateWrapper()
+        }
+    }
+}

+ 64 - 0
app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt

@@ -0,0 +1,64 @@
+package im.angry.openeuicc.core
+
+import net.typeblog.lpac_jni.EuiccInfo2
+import net.typeblog.lpac_jni.LocalProfileAssistant
+import net.typeblog.lpac_jni.LocalProfileInfo
+import net.typeblog.lpac_jni.LocalProfileNotification
+import net.typeblog.lpac_jni.ProfileDownloadCallback
+
+class LocalProfileAssistantWrapper(private val _inner: LocalProfileAssistant) :
+    LocalProfileAssistant {
+
+    private var wrapperInvalidated = false
+
+    private val lpa: LocalProfileAssistant
+        get() {
+            if (wrapperInvalidated) {
+                throw IllegalStateException("This wrapper has been invalidated")
+            }
+
+            return _inner
+        }
+
+    override val valid: Boolean
+        get() = lpa.valid
+    override val profiles: List<LocalProfileInfo>
+        get() = lpa.profiles
+    override val notifications: List<LocalProfileNotification>
+        get() = lpa.notifications
+    override val eID: String
+        get() = lpa.eID
+    override val euiccInfo2: EuiccInfo2?
+        get() = lpa.euiccInfo2
+
+    override fun setEs10xMss(mss: Byte) = lpa.setEs10xMss(mss)
+
+    override fun enableProfile(iccid: String, refresh: Boolean): Boolean =
+        lpa.enableProfile(iccid, refresh)
+
+    override fun disableProfile(iccid: String, refresh: Boolean): Boolean =
+        lpa.disableProfile(iccid, refresh)
+
+    override fun deleteProfile(iccid: String): Boolean = lpa.deleteProfile(iccid)
+
+    override fun downloadProfile(
+        smdp: String,
+        matchingId: String?,
+        imei: String?,
+        confirmationCode: String?,
+        callback: ProfileDownloadCallback
+    ): Boolean = lpa.downloadProfile(smdp, matchingId, imei, confirmationCode, callback)
+
+    override fun deleteNotification(seqNumber: Long): Boolean = lpa.deleteNotification(seqNumber)
+
+    override fun handleNotification(seqNumber: Long): Boolean = lpa.handleNotification(seqNumber)
+
+    override fun setNickname(iccid: String, nickname: String): Boolean =
+        lpa.setNickname(iccid, nickname)
+
+    override fun close() = lpa.close()
+
+    fun invalidateWrapper() {
+        wrapperInvalidated = true
+    }
+}