diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt index 756ef3c..80746c4 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt @@ -1,17 +1,24 @@ package im.angry.openeuicc.core -import im.angry.openeuicc.util.* import net.typeblog.lpac_jni.LocalProfileAssistant +// A custom type to avoid compatibility issues with UiccCardInfo / UiccPortInfo +data class EuiccChannelInfo( + val slotId: Int, + val cardId: Int, + val name: String, + val imei: String, + val removable: Boolean +) + abstract class EuiccChannel( - port: UiccPortInfoCompat + info: EuiccChannelInfo ) { - val slotId = port.card.physicalSlotIndex // PHYSICAL slot - val logicalSlotId = port.logicalSlotIndex - val portId = port.portIndex - val cardId = port.card.cardId - val name = "SLOT ${port.card.physicalSlotIndex}:${port.portIndex}" - val removable = port.card.isRemovable + val slotId = info.slotId + val cardId = info.cardId + val name = info.name + val imei = info.imei + val removable = info.removable abstract val lpa: LocalProfileAssistant val valid: Boolean diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt index 4d3c54a..f579c82 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt @@ -1,5 +1,6 @@ package im.angry.openeuicc.core +import android.annotation.SuppressLint import android.content.Context import android.os.Handler import android.os.HandlerThread @@ -16,6 +17,7 @@ import java.lang.IllegalArgumentException import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine +@SuppressLint("MissingPermission") // We rely on ARA-based privileges, not READ_PRIVILEGED_PHONE_STATE open class EuiccChannelManager(protected val context: Context) { companion object { const val TAG = "EuiccChannelManager" @@ -50,32 +52,27 @@ open class EuiccChannelManager(protected val context: Context) { } } - protected open fun tryOpenEuiccChannelPrivileged(port: UiccPortInfoCompat): EuiccChannel? { + protected open fun tryOpenEuiccChannelPrivileged(uiccInfo: UiccCardInfoCompat, channelInfo: EuiccChannelInfo): EuiccChannel? { // No-op when unprivileged return null } - protected fun tryOpenEuiccChannelUnprivileged(port: UiccPortInfoCompat): EuiccChannel? { - if (port.portIndex != 0) { - Log.w(TAG, "OMAPI channel attempted on non-zero portId, ignoring") - return null - } - - Log.i(TAG, "Trying OMAPI for physical slot ${port.card.physicalSlotIndex}") + protected fun tryOpenEuiccChannelUnprivileged(uiccInfo: UiccCardInfoCompat, channelInfo: EuiccChannelInfo): EuiccChannel? { + Log.i(TAG, "Trying OMAPI for slot ${uiccInfo.physicalSlotIndex}") try { - return OmapiChannel(seService!!, port) + return OmapiChannel(seService!!, channelInfo) } catch (e: IllegalArgumentException) { // Failed - Log.w(TAG, "OMAPI APDU interface unavailable for physical slot ${port.card.physicalSlotIndex}.") + Log.w(TAG, "OMAPI APDU interface unavailable for slot ${uiccInfo.physicalSlotIndex}.") } return null } - private suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel? { + private suspend fun tryOpenEuiccChannel(uiccInfo: UiccCardInfoCompat): EuiccChannel? { lock.withLock { ensureSEService() - val existing = channels.find { it.slotId == port.card.physicalSlotIndex && it.portId == port.portIndex } + val existing = channels.find { it.slotId == uiccInfo.physicalSlotIndex } if (existing != null) { if (existing.valid) { return existing @@ -85,10 +82,18 @@ open class EuiccChannelManager(protected val context: Context) { } } - var euiccChannel: EuiccChannel? = tryOpenEuiccChannelPrivileged(port) + val channelInfo = EuiccChannelInfo( + uiccInfo.physicalSlotIndex, + uiccInfo.cardId, + "SIM ${uiccInfo.physicalSlotIndex}", + tm.getImei(uiccInfo.physicalSlotIndex) ?: return null, + uiccInfo.isRemovable + ) + + var euiccChannel: EuiccChannel? = tryOpenEuiccChannelPrivileged(uiccInfo, channelInfo) if (euiccChannel == null) { - euiccChannel = tryOpenEuiccChannelUnprivileged(port) + euiccChannel = tryOpenEuiccChannelUnprivileged(uiccInfo, channelInfo) } if (euiccChannel != null) { @@ -99,28 +104,16 @@ open class EuiccChannelManager(protected val context: Context) { } } - fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel? = - runBlocking { - if (!checkPrivileges()) return@runBlocking null - withContext(Dispatchers.IO) { - for (card in tm.uiccCardsInfoCompat) { - for (port in card.ports) { - if (port.logicalSlotIndex == logicalSlotId) { - return@withContext tryOpenEuiccChannel(port) - } - } - } - - null - } + private suspend fun findEuiccChannelBySlot(slotId: Int): EuiccChannel? { + return tm.uiccCardsInfoCompat.find { it.physicalSlotIndex == slotId }?.let { + tryOpenEuiccChannel(it) } + } - fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel? = runBlocking { + fun findEuiccChannelBySlotBlocking(slotId: Int): EuiccChannel? = runBlocking { if (!checkPrivileges()) return@runBlocking null withContext(Dispatchers.IO) { - tm.uiccCardsInfoCompat.find { it.physicalSlotIndex == physicalSlotId }?.let { card -> - card.ports.find { it.portIndex == portId }?.let { tryOpenEuiccChannel(it) } - } + findEuiccChannelBySlot(slotId) } } @@ -131,10 +124,8 @@ open class EuiccChannelManager(protected val context: Context) { ensureSEService() for (uiccInfo in tm.uiccCardsInfoCompat) { - for (port in uiccInfo.ports) { - if (tryOpenEuiccChannel(port) != null) { - Log.d(TAG, "Found eUICC on slot ${uiccInfo.physicalSlotIndex} port ${port.portIndex}") - } + if (tryOpenEuiccChannel(uiccInfo) != null) { + Log.d(TAG, "Found eUICC on slot ${uiccInfo.physicalSlotIndex}") } } } diff --git a/app-common/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt b/app-common/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt index fb5a264..1c8dddc 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt @@ -3,7 +3,6 @@ package im.angry.openeuicc.core import android.se.omapi.Channel import android.se.omapi.SEService import android.se.omapi.Session -import im.angry.openeuicc.util.UiccPortInfoCompat import net.typeblog.lpac_jni.ApduInterface import net.typeblog.lpac_jni.LocalProfileAssistant import net.typeblog.lpac_jni.impl.HttpInterfaceImpl @@ -11,13 +10,13 @@ import net.typeblog.lpac_jni.impl.LocalProfileAssistantImpl class OmapiApduInterface( private val service: SEService, - private val port: UiccPortInfoCompat + private val info: EuiccChannelInfo ): ApduInterface { private lateinit var session: Session private lateinit var lastChannel: Channel override fun connect() { - session = service.getUiccReader(port.logicalSlotIndex + 1).openSession() + session = service.getUiccReader(info.slotId + 1).openSession() } override fun disconnect() { @@ -51,9 +50,9 @@ class OmapiApduInterface( class OmapiChannel( service: SEService, - port: UiccPortInfoCompat, -) : EuiccChannel(port) { + info: EuiccChannelInfo, +) : EuiccChannel(info) { override val lpa: LocalProfileAssistant = LocalProfileAssistantImpl( - OmapiApduInterface(service, port), + OmapiApduInterface(service, info), HttpInterfaceImpl()) } diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccChannelFragmentUtils.kt b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccChannelFragmentUtils.kt index 34f4240..5aa3d73 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccChannelFragmentUtils.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccChannelFragmentUtils.kt @@ -8,26 +8,23 @@ import im.angry.openeuicc.util.openEuiccApplication interface EuiccFragmentMarker -fun newInstanceEuicc(clazz: Class, slotId: Int, portId: Int): T where T: Fragment, T: EuiccFragmentMarker { +fun newInstanceEuicc(clazz: Class, slotId: Int): T where T: Fragment, T: EuiccFragmentMarker { val instance = clazz.newInstance() instance.arguments = Bundle().apply { putInt("slotId", slotId) - putInt("portId", portId) } return instance } val T.slotId: Int where T: Fragment, T: EuiccFragmentMarker get() = requireArguments().getInt("slotId") -val T.portId: Int where T: Fragment, T: EuiccFragmentMarker - get() = requireArguments().getInt("portId") val T.euiccChannelManager: EuiccChannelManager where T: Fragment, T: EuiccFragmentMarker get() = openEuiccApplication.euiccChannelManager val T.channel: EuiccChannel where T: Fragment, T: EuiccFragmentMarker get() = - euiccChannelManager.findEuiccChannelByPortBlocking(slotId, portId)!! + euiccChannelManager.findEuiccChannelBySlotBlocking(slotId)!! interface EuiccProfilesChangedListener { fun onEuiccProfilesChanged() diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt index e44d495..8966dbb 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt @@ -30,8 +30,8 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesCh companion object { const val TAG = "EuiccManagementFragment" - fun newInstance(slotId: Int, portId: Int): EuiccManagementFragment = - newInstanceEuicc(EuiccManagementFragment::class.java, slotId, portId) + fun newInstance(slotId: Int): EuiccManagementFragment = + newInstanceEuicc(EuiccManagementFragment::class.java, slotId) } private lateinit var swipeRefresh: SwipeRefreshLayout @@ -62,7 +62,7 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesCh LinearLayoutManager(view.context, LinearLayoutManager.VERTICAL, false) fab.setOnClickListener { - ProfileDownloadFragment.newInstance(slotId, portId) + ProfileDownloadFragment.newInstance(slotId) .show(childFragmentManager, ProfileDownloadFragment.TAG) } } @@ -195,12 +195,12 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesCh true } R.id.rename -> { - ProfileRenameFragment.newInstance(slotId, portId, profile.iccid, profile.displayName) + ProfileRenameFragment.newInstance(slotId, profile.iccid, profile.displayName) .show(childFragmentManager, ProfileRenameFragment.TAG) true } R.id.delete -> { - ProfileDeleteFragment.newInstance(slotId, portId, profile.iccid, profile.displayName) + ProfileDeleteFragment.newInstance(slotId, profile.iccid, profile.displayName) .show(childFragmentManager, ProfileDeleteFragment.TAG) true } diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt index 4bd45e4..159552c 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt @@ -88,7 +88,7 @@ open class MainActivity : AppCompatActivity() { withContext(Dispatchers.Main) { manager.knownChannels.forEach { channel -> spinnerAdapter.add(channel.name) - fragments.add(EuiccManagementFragment.newInstance(channel.slotId, channel.portId)) + fragments.add(EuiccManagementFragment.newInstance(channel.slotId)) } if (fragments.isNotEmpty()) { diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDeleteFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDeleteFragment.kt index e7b70b6..92571ad 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDeleteFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDeleteFragment.kt @@ -16,8 +16,8 @@ class ProfileDeleteFragment : DialogFragment(), EuiccFragmentMarker { companion object { const val TAG = "ProfileDeleteFragment" - fun newInstance(slotId: Int, portId: Int, iccid: String, name: String): ProfileDeleteFragment { - val instance = newInstanceEuicc(ProfileDeleteFragment::class.java, slotId, portId) + fun newInstance(slotId: Int, iccid: String, name: String): ProfileDeleteFragment { + val instance = newInstanceEuicc(ProfileDeleteFragment::class.java, slotId) instance.requireArguments().apply { putString("iccid", iccid) putString("name", name) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt index f77c2ee..b4937db 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt @@ -1,6 +1,5 @@ package im.angry.openeuicc.ui -import android.annotation.SuppressLint import android.app.Dialog import android.os.Bundle import android.text.Editable @@ -17,20 +16,19 @@ import com.google.android.material.textfield.TextInputLayout import com.journeyapps.barcodescanner.ScanContract import com.journeyapps.barcodescanner.ScanOptions import im.angry.openeuicc.common.R -import im.angry.openeuicc.util.openEuiccApplication import im.angry.openeuicc.util.setWidthPercent import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import net.typeblog.lpac_jni.ProfileDownloadCallback -import kotlin.Exception +import java.lang.Exception class ProfileDownloadFragment : DialogFragment(), EuiccFragmentMarker, Toolbar.OnMenuItemClickListener { companion object { const val TAG = "ProfileDownloadFragment" - fun newInstance(slotId: Int, portId: Int): ProfileDownloadFragment = - newInstanceEuicc(ProfileDownloadFragment::class.java, slotId, portId) + fun newInstance(slotId: Int): ProfileDownloadFragment = + newInstanceEuicc(ProfileDownloadFragment::class.java, slotId) } private lateinit var toolbar: Toolbar @@ -107,16 +105,9 @@ class ProfileDownloadFragment : DialogFragment(), EuiccFragmentMarker, Toolbar.O setWidthPercent(95) } - @SuppressLint("MissingPermission") override fun onStart() { super.onStart() - profileDownloadIMEI.editText!!.text = Editable.Factory.getInstance().newEditable( - try { - openEuiccApplication.telephonyManager.getImei(channel.logicalSlotId) - } catch (e: Exception) { - "" - } - ) + profileDownloadIMEI.editText!!.text = Editable.Factory.getInstance().newEditable(channel.imei) lifecycleScope.launch(Dispatchers.IO) { // Fetch remaining NVRAM diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileRenameFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileRenameFragment.kt index 35c5edb..d940036 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileRenameFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileRenameFragment.kt @@ -25,8 +25,8 @@ class ProfileRenameFragment : DialogFragment(), EuiccFragmentMarker { companion object { const val TAG = "ProfileRenameFragment" - fun newInstance(slotId: Int, portId: Int, iccid: String, currentName: String): ProfileRenameFragment { - val instance = newInstanceEuicc(ProfileRenameFragment::class.java, slotId, portId) + fun newInstance(slotId: Int, iccid: String, currentName: String): ProfileRenameFragment { + val instance = newInstanceEuicc(ProfileRenameFragment::class.java, slotId) instance.requireArguments().apply { putString("iccid", iccid) putString("currentName", currentName) diff --git a/app-common/src/main/java/im/angry/openeuicc/util/TelephonyCompat.kt b/app-common/src/main/java/im/angry/openeuicc/util/TelephonyCompat.kt index b89253c..7d54469 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/TelephonyCompat.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/TelephonyCompat.kt @@ -68,14 +68,6 @@ class UiccPortInfoCompat(private val _inner: Any?, val card: UiccCardInfoCompat) } else { 0 } - - val logicalSlotIndex: Int - get() = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - inner.logicalSlotIndex - } else { - card.physicalSlotIndex // logical is the same as physical below TIRAMISU - } } val TelephonyManager.uiccCardsInfoCompat: List diff --git a/app/build.gradle b/app/build.gradle index d6bad2a..accead3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -57,8 +57,6 @@ android { } dependencies { - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'androidx.recyclerview:recyclerview:1.3.2' compileOnly project(':libs:hidden-apis-stub') implementation project(':libs:hidden-apis-shim') implementation project(':libs:lpac-jni') diff --git a/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelManager.kt b/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelManager.kt index 450fc7f..f808a41 100644 --- a/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelManager.kt +++ b/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelManager.kt @@ -10,21 +10,21 @@ import java.lang.IllegalArgumentException class PrivilegedEuiccChannelManager(context: Context): EuiccChannelManager(context) { override fun checkPrivileges() = true // TODO: Implement proper system app check - override fun tryOpenEuiccChannelPrivileged(port: UiccPortInfoCompat): EuiccChannel? { - if (port.card.isRemovable) { + override fun tryOpenEuiccChannelPrivileged(uiccInfo: UiccCardInfoCompat, channelInfo: EuiccChannelInfo): EuiccChannel? { + if (uiccInfo.isRemovable) { // Attempt unprivileged (OMAPI) before TelephonyManager // but still try TelephonyManager in case OMAPI is broken - super.tryOpenEuiccChannelUnprivileged(port)?.let { return it } + super.tryOpenEuiccChannelUnprivileged(uiccInfo, channelInfo)?.let { return it } } - if (port.card.isEuicc) { - Log.i(TAG, "Trying TelephonyManager for slot ${port.card.physicalSlotIndex} port ${port.portIndex}") + if (uiccInfo.isEuicc) { + Log.i(TAG, "Trying TelephonyManager for slot ${uiccInfo.physicalSlotIndex}") // TODO: On Tiramisu, we should also connect all available "ports" for MEP support try { - return TelephonyManagerChannel(port, tm) + return TelephonyManagerChannel(channelInfo, tm) } catch (e: IllegalArgumentException) { // Failed - Log.w(TAG, "TelephonyManager APDU interface unavailable for slot ${port.card.physicalSlotIndex} port ${port.portIndex}, falling back") + Log.w(TAG, "TelephonyManager APDU interface unavailable for slot ${uiccInfo.physicalSlotIndex}, falling back") } } return null diff --git a/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduInterface.kt b/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduInterface.kt index f4dc483..e4924ea 100644 --- a/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduInterface.kt +++ b/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduInterface.kt @@ -9,7 +9,7 @@ import net.typeblog.lpac_jni.impl.HttpInterfaceImpl import net.typeblog.lpac_jni.impl.LocalProfileAssistantImpl class TelephonyManagerApduInterface( - private val port: UiccPortInfoCompat, + private val info: EuiccChannelInfo, private val tm: TelephonyManager ): ApduInterface { private var lastChannel: Int = -1 @@ -25,9 +25,9 @@ class TelephonyManagerApduInterface( override fun logicalChannelOpen(aid: ByteArray): Int { check(lastChannel == -1) { "Already initialized" } val hex = aid.encodeHex() - val channel = tm.iccOpenLogicalChannelByPortCompat(port.card.physicalSlotIndex, port.portIndex, hex, 0) + val channel = tm.iccOpenLogicalChannelBySlot(info.slotId, hex, 0) if (channel.status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR || channel.channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL) { - throw IllegalArgumentException("Cannot open logical channel $hex via TelephonManager on slot ${port.card.physicalSlotIndex} port ${port.portIndex}"); + throw IllegalArgumentException("Cannot open logical channel " + hex + " via TelephonManager on slot " + info.slotId); } lastChannel = channel.channel return lastChannel @@ -35,7 +35,7 @@ class TelephonyManagerApduInterface( override fun logicalChannelClose(handle: Int) { check(handle == lastChannel) { "Invalid channel handle " } - tm.iccCloseLogicalChannelByPortCompat(port.card.physicalSlotIndex, port.portIndex, handle) + tm.iccCloseLogicalChannelBySlot(info.slotId, handle) lastChannel = -1 } @@ -49,18 +49,18 @@ class TelephonyManagerApduInterface( val p3 = tx[4].toUByte().toInt() val p4 = tx.drop(5).toByteArray().encodeHex() - return tm.iccTransmitApduLogicalChannelByPortCompat(port.card.physicalSlotIndex, port.portIndex, lastChannel, + return tm.iccTransmitApduLogicalChannelBySlot(info.slotId, lastChannel, cla, instruction, p1, p2, p3, p4)?.decodeHex() ?: byteArrayOf() } } class TelephonyManagerChannel( - port: UiccPortInfoCompat, + info: EuiccChannelInfo, private val tm: TelephonyManager -) : EuiccChannel(port) { +) : EuiccChannel(info) { override val lpa: LocalProfileAssistant = LocalProfileAssistantImpl( - TelephonyManagerApduInterface(port, tm), + TelephonyManagerApduInterface(info, tm), HttpInterfaceImpl() ) } \ No newline at end of file diff --git a/app/src/main/java/im/angry/openeuicc/ui/PrivilegedMainActivity.kt b/app/src/main/java/im/angry/openeuicc/ui/PrivilegedMainActivity.kt index 8bcadc8..880d43b 100644 --- a/app/src/main/java/im/angry/openeuicc/ui/PrivilegedMainActivity.kt +++ b/app/src/main/java/im/angry/openeuicc/ui/PrivilegedMainActivity.kt @@ -27,10 +27,6 @@ class PrivilegedMainActivity : MainActivity() { finish() true } - R.id.slot_mapping -> { - SlotMappingFragment().show(supportFragmentManager, SlotMappingFragment.TAG) - true - } else -> super.onOptionsItemSelected(item) } } \ No newline at end of file diff --git a/app/src/main/java/im/angry/openeuicc/ui/SlotMappingFragment.kt b/app/src/main/java/im/angry/openeuicc/ui/SlotMappingFragment.kt deleted file mode 100644 index 8e3fe0e..0000000 --- a/app/src/main/java/im/angry/openeuicc/ui/SlotMappingFragment.kt +++ /dev/null @@ -1,159 +0,0 @@ -package im.angry.openeuicc.ui - -import android.annotation.SuppressLint -import android.os.Bundle -import android.telephony.TelephonyManager -import android.telephony.UiccSlotMapping -import android.view.LayoutInflater -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup -import android.widget.AdapterView -import android.widget.AdapterView.OnItemSelectedListener -import android.widget.ArrayAdapter -import android.widget.Spinner -import android.widget.TextView -import androidx.appcompat.widget.Toolbar -import androidx.appcompat.widget.Toolbar.OnMenuItemClickListener -import androidx.fragment.app.DialogFragment -import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import im.angry.openeuicc.OpenEuiccApplication -import im.angry.openeuicc.R -import im.angry.openeuicc.util.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -class SlotMappingFragment: DialogFragment(), OnMenuItemClickListener { - companion object { - const val TAG = "SlotMappingFragment" - } - - private val tm: TelephonyManager by lazy { - (requireContext().applicationContext as OpenEuiccApplication).telephonyManager - } - - private val ports: List by lazy { - tm.uiccCardsInfoCompat.flatMap { it.ports } - } - - private val portsDesc: List by lazy { - ports.map { getString(R.string.slot_mapping_port, it.card.physicalSlotIndex, it.portIndex) } - } - - private lateinit var toolbar: Toolbar - private lateinit var recyclerView: RecyclerView - private lateinit var adapter: SlotMappingAdapter - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - val view = inflater.inflate(R.layout.fragment_slot_mapping, container, false) - toolbar = view.findViewById(R.id.toolbar) - toolbar.inflateMenu(R.menu.fragment_slot_mapping) - recyclerView = view.findViewById(R.id.mapping_list) - recyclerView.layoutManager = - LinearLayoutManager(view.context, LinearLayoutManager.VERTICAL, false) - return view - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - toolbar.title = getString(R.string.slot_mapping) - toolbar.setNavigationOnClickListener { dismiss() } - toolbar.setOnMenuItemClickListener(this) - } - - override fun onResume() { - super.onResume() - setWidthPercent(85) - init() - } - - @SuppressLint("NotifyDataSetChanged") - private fun init() { - lifecycleScope.launch(Dispatchers.Main) { - val mapping = withContext(Dispatchers.IO) { - tm.simSlotMapping - } - - adapter = SlotMappingAdapter(mapping.toMutableList().apply { - sortBy { it.logicalSlotIndex } - }) - recyclerView.adapter = adapter - adapter.notifyDataSetChanged() - - } - } - - private fun commit() { - lifecycleScope.launch(Dispatchers.Main) { - withContext(Dispatchers.IO) { - tm.simSlotMapping = adapter.mappings - } - openEuiccApplication.euiccChannelManager.invalidate() - requireActivity().finish() - } - } - - override fun onMenuItemClick(item: MenuItem?): Boolean = - when (item!!.itemId) { - R.id.ok -> { - commit() - true - } - else -> false - } - - inner class ViewHolder(root: View): RecyclerView.ViewHolder(root), OnItemSelectedListener { - private val textViewLogicalSlot: TextView = root.findViewById(R.id.slot_mapping_logical_slot) - private val spinnerPorts: Spinner = root.findViewById(R.id.slot_mapping_ports) - - init { - spinnerPorts.adapter = ArrayAdapter(requireContext(), im.angry.openeuicc.common.R.layout.spinner_item, portsDesc) - spinnerPorts.onItemSelectedListener = this - } - - private lateinit var mappings: MutableList - private var mappingId: Int = -1 - - fun attachView(mappings: MutableList, mappingId: Int) { - this.mappings = mappings - this.mappingId = mappingId - - textViewLogicalSlot.text = getString(R.string.slot_mapping_logical_slot, mappings[mappingId].logicalSlotIndex) - spinnerPorts.setSelection(ports.indexOfFirst { - it.card.physicalSlotIndex == mappings[mappingId].physicalSlotIndex - && it.portIndex == mappings[mappingId].portIndex - }) - } - - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - check(this::mappings.isInitialized) { "mapping not assigned" } - mappings[mappingId] = - UiccSlotMapping( - ports[position].portIndex, ports[position].card.physicalSlotIndex, mappings[mappingId].logicalSlotIndex) - } - - override fun onNothingSelected(parent: AdapterView<*>?) { - - } - } - - inner class SlotMappingAdapter(val mappings: MutableList): RecyclerView.Adapter() { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val view = LayoutInflater.from(parent.context).inflate(R.layout.fragment_slot_mapping_item, parent, false) - return ViewHolder(view) - } - - override fun getItemCount(): Int = mappings.size - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.attachView(mappings, position) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyCompat.kt b/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyCompat.kt index ac4c968..a1a503d 100644 --- a/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyCompat.kt +++ b/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyCompat.kt @@ -4,7 +4,7 @@ import android.os.Build import android.telephony.IccOpenLogicalChannelResponse import android.telephony.TelephonyManager -// TODO: Usage of new APIs from T or later will still break build in-tree on lower AOSP versions +// TODO: Usage of *byPort APIs will still break build in-tree on lower AOSP versions // Maybe older versions should simply include hidden-apis-shim when building? fun TelephonyManager.iccOpenLogicalChannelByPortCompat( slotIndex: Int, portIndex: Int, aid: String?, p2: Int diff --git a/app/src/main/res/layout/fragment_slot_mapping.xml b/app/src/main/res/layout/fragment_slot_mapping.xml deleted file mode 100644 index 469263b..0000000 --- a/app/src/main/res/layout/fragment_slot_mapping.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_slot_mapping_item.xml b/app/src/main/res/layout/fragment_slot_mapping_item.xml deleted file mode 100644 index 44c154e..0000000 --- a/app/src/main/res/layout/fragment_slot_mapping_item.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/activity_main_privileged.xml b/app/src/main/res/menu/activity_main_privileged.xml index cbfaf31..2bbe20f 100644 --- a/app/src/main/res/menu/activity_main_privileged.xml +++ b/app/src/main/res/menu/activity_main_privileged.xml @@ -7,8 +7,4 @@ android:checkable="true" android:visible="false" app:showAsAction="never" /> - \ No newline at end of file diff --git a/app/src/main/res/menu/fragment_slot_mapping.xml b/app/src/main/res/menu/fragment_slot_mapping.xml deleted file mode 100644 index 7f03e18..0000000 --- a/app/src/main/res/menu/fragment_slot_mapping.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8e1c4ba..280289a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,8 +5,4 @@ Dual SIM DSDS state switched. Please wait until the modem restarts. - - Slot Mapping - Logical slot %d: - Slot %d Port %d \ No newline at end of file diff --git a/libs/hidden-apis-shim/build.gradle b/libs/hidden-apis-shim/build.gradle index a3a0472..1c7a212 100644 --- a/libs/hidden-apis-shim/build.gradle +++ b/libs/hidden-apis-shim/build.gradle @@ -31,7 +31,7 @@ android { } dependencies { - compileOnly project(':libs:hidden-apis-stub') + implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.4.2' implementation 'com.google.android.material:material:1.6.1' diff --git a/libs/hidden-apis-shim/src/main/java/im/angry/openeuicc/util/TelephonyManagerHiddenApi.kt b/libs/hidden-apis-shim/src/main/java/im/angry/openeuicc/util/TelephonyManagerHiddenApi.kt index 4203fea..4a9ea64 100644 --- a/libs/hidden-apis-shim/src/main/java/im/angry/openeuicc/util/TelephonyManagerHiddenApi.kt +++ b/libs/hidden-apis-shim/src/main/java/im/angry/openeuicc/util/TelephonyManagerHiddenApi.kt @@ -3,7 +3,6 @@ package im.angry.openeuicc.util import android.telephony.IccOpenLogicalChannelResponse import android.telephony.SubscriptionManager import android.telephony.TelephonyManager -import android.telephony.UiccSlotMapping import java.lang.reflect.Method // Hidden APIs via reflection to enable building without AOSP source tree @@ -47,17 +46,6 @@ private val iccTransmitApduLogicalChannelByPort: Method by lazy { Int::class.java, Int::class.java, Int::class.java, String::class.java ) } -private val getSimSlotMapping: Method by lazy { - TelephonyManager::class.java.getMethod( - "getSimSlotMapping" - ) -} -private val setSimSlotMapping: Method by lazy { - TelephonyManager::class.java.getMethod( - "setSimSlotMapping", - Collection::class.java - ) -} fun TelephonyManager.iccOpenLogicalChannelBySlot( slotId: Int, appletId: String?, p2: Int @@ -91,10 +79,6 @@ fun TelephonyManager.iccTransmitApduLogicalChannelByPort( this, slotId, portId, channel, cla, instruction, p1, p2, p3, data ) as String? -var TelephonyManager.simSlotMapping: Collection - get() = getSimSlotMapping.invoke(this) as Collection - set(new) { setSimSlotMapping.invoke(this, new) } - private val requestEmbeddedSubscriptionInfoListRefresh: Method by lazy { SubscriptionManager::class.java.getMethod("requestEmbeddedSubscriptionInfoListRefresh", Int::class.java) } diff --git a/libs/hidden-apis-stub/src/main/java/android/telephony/UiccSlotMapping.java b/libs/hidden-apis-stub/src/main/java/android/telephony/UiccSlotMapping.java deleted file mode 100644 index e3ea60e..0000000 --- a/libs/hidden-apis-stub/src/main/java/android/telephony/UiccSlotMapping.java +++ /dev/null @@ -1,73 +0,0 @@ -package android.telephony; - -import android.os.Parcel; -import android.os.Parcelable; - -public final class UiccSlotMapping implements Parcelable { - public static final Creator CREATOR = null; - - @Override - public void writeToParcel(Parcel dest, int flags) { - throw new RuntimeException("stub"); - } - - @Override - public int describeContents() { - return 0; - } - - /** - * - * @param portIndex The port index is an enumeration of the ports available on the UICC. - * @param physicalSlotIndex is unique index referring to a physical SIM slot. - * @param logicalSlotIndex is unique index referring to a logical SIM slot. - * - */ - public UiccSlotMapping(int portIndex, int physicalSlotIndex, int logicalSlotIndex) { - throw new RuntimeException("stub"); - } - - /** - * Port index is the unique index referring to a port belonging to the physical SIM slot. - * If the SIM does not support multiple enabled profiles, the port index is default index 0. - * - * @return port index. - */ - public int getPortIndex() { - throw new RuntimeException("stub"); - } - - /** - * Gets the physical slot index for the slot that the UICC is currently inserted in. - * - * @return physical slot index which is the index of actual physical UICC slot. - */ - public int getPhysicalSlotIndex() { - throw new RuntimeException("stub"); - } - - /** - * Gets logical slot index for the slot that the UICC is currently attached. - * Logical slot index is the unique index referring to a logical slot(logical modem stack). - * - * @return logical slot index; - */ - public int getLogicalSlotIndex() { - throw new RuntimeException("stub"); - } - - @Override - public boolean equals(Object obj) { - throw new RuntimeException("stub"); - } - - @Override - public int hashCode() { - throw new RuntimeException("stub"); - } - - @Override - public String toString() { - throw new RuntimeException("stub"); - } -}