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 f918494..b3f42b5 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 @@ -9,7 +9,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import net.typeblog.lpac_jni.ApduInterface -import java.util.concurrent.atomic.AtomicInteger class OmapiApduInterface( private val service: SEService, @@ -21,8 +20,12 @@ class OmapiApduInterface( } private lateinit var session: Session - private val index = AtomicInteger(0) - private val channels = mutableMapOf() + private val channels = arrayOf( + null, + null, + null, + null, + ) override val valid: Boolean get() = service.isConnected && (this::session.isInitialized && !session.isClosed) @@ -41,20 +44,21 @@ class OmapiApduInterface( override fun logicalChannelOpen(aid: ByteArray): Int { val channel = session.openLogicalChannel(aid) check(channel != null) { "Failed to open logical channel (${aid.encodeHex()})" } - val handle = index.incrementAndGet() - synchronized(channels) { channels[handle] = channel } - return handle + val index = channels.indexOf(null) + check(index != -1) { "No free logical channel slots" } + synchronized(channels) { channels[index] = channel } + return index } override fun logicalChannelClose(handle: Int) { - val channel = channels[handle] + val channel = channels.getOrNull(handle) check(channel != null) { "Invalid logical channel handle $handle" } if (channel.isOpen) channel.close() - synchronized(channels) { channels.remove(handle) } + synchronized(channels) { channels[handle] = null } } override fun transmit(handle: Int, tx: ByteArray): ByteArray { - val channel = channels[handle] + val channel = channels.getOrNull(handle) check(channel != null) { "Invalid logical channel handle $handle" } if (runBlocking { verboseLoggingFlow.first() }) { diff --git a/app-common/src/main/res/xml/locale_config.xml b/app-common/src/main/res/xml/locale_config.xml index 6d7f076..e1a13f8 100644 --- a/app-common/src/main/res/xml/locale_config.xml +++ b/app-common/src/main/res/xml/locale_config.xml @@ -3,5 +3,4 @@ - \ No newline at end of file diff --git a/app-unpriv/src/main/java/im/angry/openeuicc/ui/UnprivilegedEuiccManagementFragment.kt b/app-unpriv/src/main/java/im/angry/openeuicc/ui/UnprivilegedEuiccManagementFragment.kt index 617cbec..fad03fd 100644 --- a/app-unpriv/src/main/java/im/angry/openeuicc/ui/UnprivilegedEuiccManagementFragment.kt +++ b/app-unpriv/src/main/java/im/angry/openeuicc/ui/UnprivilegedEuiccManagementFragment.kt @@ -1,10 +1,7 @@ package im.angry.openeuicc.ui -import android.content.pm.PackageManager -import android.provider.Settings import android.view.Menu import android.view.MenuInflater -import android.widget.Toast import im.angry.easyeuicc.R import im.angry.openeuicc.util.SIMToolkit import im.angry.openeuicc.util.newInstanceEuicc @@ -27,22 +24,8 @@ class UnprivilegedEuiccManagementFragment : EuiccManagementFragment() { super.onCreateOptionsMenu(menu, inflater) inflater.inflate(R.menu.fragment_sim_toolkit, menu) menu.findItem(R.id.open_sim_toolkit).apply { - val slot = stk[slotId] ?: return@apply - isVisible = slot.intent != null - setOnMenuItemClickListener { - val intent = slot.intent ?: return@setOnMenuItemClickListener false - if (intent.action == Settings.ACTION_APPLICATION_DETAILS_SETTINGS) { - val packageName = intent.data!!.schemeSpecificPart - val label = requireContext().packageManager.getApplicationLabel(packageName) - val message = requireContext().getString(R.string.toast_prompt_to_enable_sim_toolkit, label) - Toast.makeText(context, message, Toast.LENGTH_LONG).show() - } - startActivity(intent) - true - } + isVisible = stk.isAvailable(slotId) + intent = stk.intent(slotId) } } -} - -private fun PackageManager.getApplicationLabel(packageName: String): CharSequence = - getApplicationLabel(getApplicationInfo(packageName, 0)) +} \ No newline at end of file diff --git a/app-unpriv/src/main/java/im/angry/openeuicc/util/SIMToolkit.kt b/app-unpriv/src/main/java/im/angry/openeuicc/util/SIMToolkit.kt index 99824ff..ced813a 100644 --- a/app-unpriv/src/main/java/im/angry/openeuicc/util/SIMToolkit.kt +++ b/app-unpriv/src/main/java/im/angry/openeuicc/util/SIMToolkit.kt @@ -3,87 +3,65 @@ package im.angry.openeuicc.util import android.content.ComponentName import android.content.Context import android.content.Intent -import android.content.pm.ActivityInfo import android.content.pm.PackageManager -import android.net.Uri -import android.provider.Settings -import android.widget.Toast import androidx.annotation.ArrayRes import im.angry.easyeuicc.R import im.angry.openeuicc.core.EuiccChannelManager class SIMToolkit(private val context: Context) { + private val slotSelection = getComponentNames(R.array.sim_toolkit_slot_selection) + private val slots = buildMap { - fun getComponentNames(@ArrayRes id: Int) = context.resources - .getStringArray(id).mapNotNull(ComponentName::unflattenFromString) - put(-1, getComponentNames(R.array.sim_toolkit_slot_selection)) put(0, getComponentNames(R.array.sim_toolkit_slot_1)) put(1, getComponentNames(R.array.sim_toolkit_slot_2)) } - operator fun get(slotId: Int): Slot? = when (slotId) { - -1, EuiccChannelManager.USB_CHANNEL_ID -> null - else -> Slot(context.packageManager, buildSet { - addAll(slots.getOrDefault(slotId, emptySet())) - addAll(slots.getOrDefault(-1, emptySet())) - }) + private val packageNames = buildSet { + addAll(slotSelection.map { it.packageName }) + addAll(slots.values.flatten().map { it.packageName }) } - data class Slot(private val packageManager: PackageManager, private val components: Set) { - private val packageNames: Iterable - get() = components.map(ComponentName::getPackageName).toSet() + private val activities = packageNames.flatMap(::getActivities).toSet() - private val launchIntent: Intent? - get() = packageNames.firstNotNullOfOrNull(packageManager::getLaunchIntent) + private val launchIntent by lazy { + packageNames.firstNotNullOfOrNull(::getLaunchIntent) + } - private val activities: Iterable - get() = packageNames.flatMap(packageManager::getActivities) - .filter(ActivityInfo::exported).map { ComponentName(it.packageName, it.name) } + private fun getLaunchIntent(packageName: String) = try { + val pm = context.packageManager + pm.getLaunchIntentForPackage(packageName) + } catch (_: PackageManager.NameNotFoundException) { + null + } - private fun getActivityIntent(): Intent? { - for (activity in activities) { - if (!components.contains(activity)) continue - if (isDisabledState(packageManager.getComponentEnabledSetting(activity))) continue - return Intent.makeMainActivity(activity) - } - return launchIntent + private fun getActivities(packageName: String): List { + return try { + val pm = context.packageManager + val packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES) + val activities = packageInfo.activities + if (activities.isNullOrEmpty()) return emptyList() + activities.filter { it.exported }.map { ComponentName(it.packageName, it.name) } + } catch (_: PackageManager.NameNotFoundException) { + emptyList() } + } - private fun getDisabledPackageIntent(): Intent? { - val disabledPackageName = packageNames.find { - try { - isDisabledState(packageManager.getApplicationEnabledSetting(it)) - } catch (_: IllegalArgumentException) { - false - } - } - if (disabledPackageName == null) return null - return Intent( - Settings.ACTION_APPLICATION_DETAILS_SETTINGS, - Uri.fromParts("package", disabledPackageName, null) - ) + private fun getComponentNames(@ArrayRes id: Int) = + context.resources.getStringArray(id).mapNotNull(ComponentName::unflattenFromString) + + fun isAvailable(slotId: Int) = when (slotId) { + -1 -> false + EuiccChannelManager.USB_CHANNEL_ID -> false + else -> intent(slotId) != null + } + + fun intent(slotId: Int): Intent? { + val components = slots.getOrDefault(slotId, emptySet()) + slotSelection + val intent = Intent(Intent.ACTION_MAIN, null).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK + component = components.find(activities::contains) + addCategory(Intent.CATEGORY_LAUNCHER) } - - val intent: Intent? - get() = getActivityIntent() ?: getDisabledPackageIntent() + return if (intent.component != null) intent else launchIntent } } - -private fun isDisabledState(state: Int) = when (state) { - PackageManager.COMPONENT_ENABLED_STATE_DISABLED -> true - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER -> true - else -> false -} - -private fun PackageManager.getLaunchIntent(packageName: String) = try { - getLaunchIntentForPackage(packageName) -} catch (_: PackageManager.NameNotFoundException) { - null -} - -private fun PackageManager.getActivities(packageName: String) = try { - getPackageInfo(packageName, PackageManager.GET_ACTIVITIES) - .activities?.toList() ?: emptyList() -} catch (_: PackageManager.NameNotFoundException) { - emptyList() -} diff --git a/app-unpriv/src/main/res/values/strings.xml b/app-unpriv/src/main/res/values/strings.xml index 43bf44f..afed295 100644 --- a/app-unpriv/src/main/res/values/strings.xml +++ b/app-unpriv/src/main/res/values/strings.xml @@ -9,7 +9,6 @@ ARA-M SHA-1 copied to clipboard - Please ENABLE your \"%s\" application System Features