You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android调用SIM卡Applet触发java.lang.SecurityException问题求助

解决Android非Root应用访问SIM卡Applet的SecurityException问题

为什么Telephony API走不通

android.permission.MODIFY_PHONE_STATE是signature|privileged级别的系统权限,只有带系统签名的应用或预装在/system/priv-app目录的应用才能获取,普通应用常规安装根本拿不到,所以这个路子直接放弃。

OMAPI的正确用法(普通应用可用)

OMAPI(Open Mobile API)是Android官方提供的安全元素访问标准API,普通应用只要配置正确就能用,不用Root。

1. 清单文件配置

在AndroidManifest.xml里加必要的权限和服务声明:

<!-- NFC权限(部分设备需要) -->
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="false" />
<!-- 部分设备需要的安全设置权限 -->
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />

<!-- OMAPI服务声明 -->
<service android:name=".SeAccessService"
         android:exported="true"
         android:permission="com.android.se.permission.SECURE_ELEMENT_SERVICE">
    <intent-filter>
        <action android:name="android.se.omapi.SE_SERVICE" />
    </intent-filter>
</service>

2. 核心实现代码

import android.se.omapi.Reader;
import android.se.omapi.SEService;
import android.se.omapi.Session;
import android.se.omapi.Channel;
import android.content.Context;

public class SeAccessManager {
    private SEService seService;

    public void init(Context context) {
        // 初始化SE服务,回调在后台线程执行
        seService = new SEService(context, new SEService.CallBack() {
            @Override
            public void serviceConnected() {
                // 遍历读卡器,找到SIM卡对应的设备
                for (Reader reader : seService.getReaders()) {
                    if (reader.getName().contains("SIM")) {
                        accessSimApplet(reader);
                        break;
                    }
                }
            }

            @Override
            public void serviceDisconnected() {
                // 服务断开后的清理
                if (seService != null) {
                    seService.shutdown();
                    seService = null;
                }
            }
        });
    }

    private void accessSimApplet(Reader reader) {
        try {
            // 打开会话
            Session session = reader.openSession();
            // 替换成你的Applet AID(字节数组格式)
            byte[] appletAid = new byte[]{0xA0, 0x00, 0x00, 0x00, 0x01, 0x01};
            // 打开逻辑通道
            Channel channel = session.openLogicalChannel(appletAid);
            
            // 发送APDU指令,替换成你的业务指令
            byte[] apduCommand = new byte[]{0x00, 0xA4, 0x04, 0x00, 0x06, 0xA0, 0x00, 0x00, 0x00, 0x01, 0x01};
            byte[] response = channel.transmit(apduCommand);
            
            // 处理返回结果,转十六进制方便查看
            String respHex = bytesToHex(response);
            // TODO: 这里处理你的业务逻辑
            
            // 关闭资源,避免内存泄漏
            channel.close();
            session.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 字节数组转十六进制字符串的工具方法
    private String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString().trim();
    }
}

3. 避坑要点

  • AID必须准确:传入的AID必须和SIM卡上Applet的AID完全一致,格式为字节数组,错一个字节都可能触发权限异常。
  • 权限补充:部分设备需要WRITE_SECURE_SETTINGS权限,普通应用可以通过ADB命令授予:
    adb shell pm grant com.your.package.name android.permission.WRITE_SECURE_SETTINGS
    
  • 线程注意:OMAPI的所有操作都不能在主线程执行,必须放在后台线程(回调本身就在后台线程,所以上面的代码没问题),否则不仅会ANR,还可能触发权限相关异常。
  • 设备兼容性:运营商定制ROM可能会限制第三方应用访问SIM卡Applet,这种情况只能联系运营商解除限制,没有通用绕过方法。

备用方案:SIM卡工具包(STK)

如果OMAPI被严格限制,可以尝试通过STK接口交互,但STK的功能依赖运营商支持,且普通应用只能调用系统提供的STK交互接口,无法直接发送自定义APDU,开发复杂度较高。


内容的提问来源于stack exchange,提问作者alireza jamshidi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 21:46:35