如何从手机访问SIM卡上的BIP/TCP服务器?
如何从手机访问SIM卡上的BIP/TCP服务器
问题背景
开发JavaCard小程序时,通过BIP在端口12345开启了TCP服务器通道,但Android(需兼容iPhone)端的普通TCP客户端无法建立连接,尝试127.0.0.1、192.169.0.1/2、localuicc均失败,执行ping localuicc提示未知主机,系统路由表也未显示SIM卡相关子网。
JavaCard端实现代码
import static uicc.toolkit.ToolkitConstants.*; import uicc.toolkit.*; import javacard.framework.*; public class NetworkApplet extends Applet implements ToolkitInterface { static final short TCP_TX_BUFFER = Byte.MAX_VALUE * 2; static final short PORT_SERVER = 12345; static final byte IDX_ID_SERVER_CHANNEL = 0; byte[] ids; @SuppressWarnings("unused") public static void install(byte[] bArray, short bOffset, byte bLength) throws SystemException { NetworkApplet applet = new NetworkApplet(bArray, bOffset, bLength); applet.register(); applet.setupToolkitRegistry(); } NetworkApplet(byte[] bArray, short bOffset, byte bLength) { ids = JCSystem.makeTransientByteArray((short) 2, JCSystem.CLEAR_ON_RESET); } void setupToolkitRegistry() { ToolkitRegistry registry = ToolkitRegistrySystem.getEntry(); // snip registry.setEvent(EVENT_PROFILE_DOWNLOAD); } @Override public void process(APDU apdu) throws ISOException { // Unused } @Override public void processToolkit(short event) throws ToolkitException { switch (event) { case EVENT_PROFILE_DOWNLOAD: ids[IDX_ID_SERVER_CHANNEL] = openServerChannel(PORT_SERVER, TCP_TX_BUFFER); break; // snip } } private static byte openServerChannel(short port, short txCapacity) { ProactiveHandler handler = ProactiveHandlerSystem.getTheHandler(); ProactiveResponseHandler responseHandler = ProactiveResponseHandlerSystem.getTheHandler(); handler.init(PRO_CMD_OPEN_CHANNEL, (byte) 0, DEV_ID_TERMINAL); handler.appendTLV(TAG_BUFFER_SIZE, txCapacity); handler.appendTLV(TAG_UICC_TERMINAL_TRANSPORT_LEVEL, QUAL_TCP_SERVER, port); if (handler.send() == RES_CMD_PERF) { return responseHandler.getChannelIdentifier(); } else { return 0; } } }
核心问题分析
- BIP并非普通IP网络:SIM卡的BIP通道是终端与UICC之间的专用逻辑通道,不依赖手机的普通IP栈,普通Socket API无法直接访问。
localuicc解析限制:TS 102485规范中localuicc仅针对终端的专用BIP接口生效,普通系统工具(如ping)无法识别,且BIP不支持ICMP协议,因此ping必然失败。- 路由表无相关条目:SIM卡的BIP通道不会暴露在系统路由表中,无需通过IP路由方式访问。
解决方案
Android端实现
Android需使用TelephonyManager的BIP专用API(需系统级权限或运营商签名):
- 声明权限:在
AndroidManifest.xml中添加:<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> - 连接BIP通道:
注:部分设备可能需要运营商定制SDK或系统级权限才能调用BIP接口。TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); UiccManager uiccManager = telephonyManager.getUiccManager(); UiccCard uiccCard = uiccManager.getUiccCardForSlotId(0); // 0为目标SIM卡槽ID if (uiccCard != null) { UiccApplication app = uiccCard.getApplication(UiccApplication.AppType.AID, "你的小程序AID"); if (app != null) { BiPChannel channel = app.openBiPChannel(BiPChannel.PROTOCOL_TCP, 12345); if (channel != null) { InputStream in = channel.getInputStream(); OutputStream out = channel.getOutputStream(); // 数据交互逻辑 channel.close(); } } }
iOS端实现
iOS需使用CoreTelephony框架的专用BIP接口(需苹果开发者账号及运营商权限):
- 导入框架:
import CoreTelephony - 连接BIP通道:
注:iOS的BIP接口仅对合作方开放,需联系苹果及运营商获取权限。let cellularData = CTCellularData() cellularData.cellularDataRestrictionDidUpdateNotifier = { status in if status == .notRestricted { let uiccManager = CTUICCManager.shared() if let uiccCard = uiccManager.uiccCards?.first { if let app = uiccCard.applications?.first(where: { $0.aid == "你的小程序AID" }) { app.openBiPChannel(protocol: .tcp, port: 12345) { channel, error in if let channel = channel { let inputStream = channel.inputStream let outputStream = channel.outputStream // 数据交互逻辑 channel.close() } } } } } }
JavaCard端补充优化
- 校验通道开启状态:在
openServerChannel中确保RES_CMD_PERF返回值正确,确认通道ID有效。 - 监听数据事件:添加
EVENT_DATA_AVAILABLE事件监听,处理终端发送的数据:@Override public void processToolkit(short event) throws ToolkitException { switch (event) { case EVENT_PROFILE_DOWNLOAD: ids[IDX_ID_SERVER_CHANNEL] = openServerChannel(PORT_SERVER, TCP_TX_BUFFER); break; case EVENT_DATA_AVAILABLE: ProactiveHandler handler = ProactiveHandlerSystem.getTheHandler(); byte channelId = handler.getChannelIdentifier(); if (channelId == ids[IDX_ID_SERVER_CHANNEL]) { byte[] buffer = new byte[TCP_TX_BUFFER]; short len = handler.copyValue(buffer, (short)0); // 数据处理逻辑 } break; // snip } }
内容的提问来源于stack exchange,提问作者Typhaon
相关产品推荐
相关产品推荐

